- |
+ |
${icon}#${rank}
|
-
-
-
- ${amb.name || 'Anonymous'}
- ${amb.ambassadorId || '---'}
+
+
+ 
+
+
+ ${amb.name || 'Anonymous'}
+ ${badgeHtml}
+
+
+ ${amb.ambassadorId || '---'}
+ • ${amb.college || '---'}
+
+
|
- ${amb.college || '---'} |
-
-
+ | ${amb.college || '---'} |
+
+
${amb.verifiedRegistrations || 0}
|
diff --git a/auth.js b/auth.js
index fc7da80..762c28c 100644
--- a/auth.js
+++ b/auth.js
@@ -255,7 +255,7 @@
}
}
- const avatarUrl = user.photoURL || './public/LogoOmnikon.jpeg';
+ const avatarUrl = user.photoURL || '/LogoOmnikon.jpeg';
authWidget.innerHTML = `

@@ -272,7 +272,7 @@
if (window.showGlobalLoader) window.showGlobalLoader('LOGGING_OUT...');
await auth.signOut();
sessionStorage.clear();
- window.location.href = '/index.html';
+ window.location.href = '/pages/index.html';
} catch(err) {
console.error("Logout error:", err);
if (window.hideGlobalLoader) window.hideGlobalLoader();
diff --git a/chatbot 2.js b/chatbot 2.js
deleted file mode 100644
index 2b1c157..0000000
--- a/chatbot 2.js
+++ /dev/null
@@ -1,212 +0,0 @@
-(() => {
- const modal = document.getElementById('chatbot-modal');
- if (!modal) return;
-
- modal.innerHTML = `
-
-
-
-
-
- `;
-
- const toggleBtn = document.getElementById('chatbot-toggle');
- const closeBtn = document.getElementById('chatbot-close');
- const sendBtn = document.getElementById('chatbot-send');
- const input = document.getElementById('chatbot-text');
- const messages = document.getElementById('chatbot-messages');
-
- let orgData = null;
- let chatHistory = [];
- let isLoaded = false;
- let hasGreeted = false;
-
- async function loadOrgData() {
- try {
- const res = await fetch('github_summary.json');
- if (res.ok) {
- orgData = await res.json();
- }
- } catch (e) {
- console.warn('Could not load github_summary.json for chatbot context', e);
- }
- }
-
- function formatMarkdown(text) {
- let escaped = text
- .replace(/&/g, "&")
- .replace(//g, ">")
- .replace(/"/g, """)
- .replace(/'/g, "'");
-
- escaped = escaped.replace(/\*\*(.*?)\*\*/g, ' $1');
- escaped = escaped.replace(/`(.*?)`/g, ' $1');
- escaped = escaped.replace(/\[(.*?)\]\((.*?)\)/g, ' $1');
-
- const lines = escaped.split('\n');
- const formattedLines = lines.map(line => {
- const trimmed = line.trim();
- if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) {
- return ` ${trimmed.substring(2)}`;
- }
- return line;
- });
-
- return formattedLines.join(' ');
- }
-
- function getSystemPrompt() {
- let context = '';
- if (orgData) {
- const projects = orgData.repos
- ? orgData.repos.map(r => `- ${r.name}: ${r.description || 'No description'} (${r.stars} stars, URL: ${r.html_url})`).join('\n')
- : '';
- const members = orgData.members
- ? orgData.members.map(m => `- ${m.login} (${m.html_url})`).join('\n')
- : '';
- context = `Here is the current real-time data about the Omnikon organization:\n\nActive Projects:\n${projects}\n\nKey Members/Contributors:\n${members}`;
- }
-
- return `You are the official AI Assistant for the Omnikon Open Source Organization.
-Your sole purpose is to help users learn about the Omnikon community, its active projects, repositories, code of conduct, guidelines, and contributors.
-
-${context}
-
-RULES:
-1. ONLY answer questions related to Omnikon, its projects, and its community.
-2. If a query is unrelated to Omnikon (e.g. general knowledge, unrelated coding, general questions), politely refuse to answer. Say: "I am the Omnikon assistant, and I can only answer questions related to the organization."
-3. Keep answers technical, concise, clear, and direct.
-4. Respond in Markdown format where appropriate.`;
- }
-
- const openModal = async () => {
- modal.classList.add('open');
- modal.classList.remove('hidden');
- input.focus();
-
- if (!isLoaded) {
- await loadOrgData();
- isLoaded = true;
- }
-
- if (!hasGreeted) {
- appendMessage('Hello! I am the Omnikon AI Assistant. I can help you with questions about our active projects, guidelines, contributors, or how to get started in our community. What would you like to know?', 'bot');
- hasGreeted = true;
- }
- };
-
- const closeModal = () => {
- modal.classList.remove('open');
- setTimeout(() => modal.classList.add('hidden'), 300);
- };
-
- toggleBtn.addEventListener('click', openModal);
- closeBtn.addEventListener('click', closeModal);
-
- const appendMessage = (text, role) => {
- const msg = document.createElement('div');
- msg.className = `chatbot-msg ${role}`;
-
- if (role === 'bot') {
- msg.innerHTML = formatMarkdown(text);
- } else {
- msg.textContent = text;
- }
-
- messages.appendChild(msg);
- messages.scrollTop = messages.scrollHeight;
- };
-
- const showTypingIndicator = () => {
- const indicator = document.createElement('div');
- indicator.className = 'chatbot-msg bot typing-indicator';
- indicator.id = 'chatbot-typing';
- indicator.innerHTML = `
-
-
-
- `;
- messages.appendChild(indicator);
- messages.scrollTop = messages.scrollHeight;
- };
-
- const removeTypingIndicator = () => {
- const indicator = document.getElementById('chatbot-typing');
- if (indicator) {
- indicator.remove();
- }
- };
-
- const getHFToken = () => window.env?.HF_TOKEN || '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';
-
- const sendMessage = async () => {
- const userText = input.value.trim();
- if (!userText) return;
-
- appendMessage(userText, 'user');
- input.value = '';
-
- if (!window.envLoaded) {
- showTypingIndicator();
- await new Promise(resolve => window.addEventListener('envLoaded', resolve, { once: true }));
- removeTypingIndicator();
- }
-
- const token = getHFToken();
- if (!token) {
- appendMessage('API key missing. Please add HF_TOKEN to your .env file in the repository root to enable the AI assistant.', 'bot');
- return;
- }
-
- showTypingIndicator();
-
- const recentHistory = chatHistory.slice(-6).map(msg => ({
- role: msg.role,
- content: msg.content
- }));
-
- const payload = {
- model: 'meta-llama/Llama-3.1-8B-Instruct:novita',
- messages: [
- { role: 'system', content: getSystemPrompt() },
- ...recentHistory,
- { role: 'user', content: userText }
- ]
- };
-
- try {
- const resp = await fetch('https://router.huggingface.co/v1/chat/completions', {
- method: 'POST',
- headers: {
- Authorization: `Bearer ${token}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(payload)
- });
-
- removeTypingIndicator();
-
- if (!resp.ok) throw new Error('HuggingFace request failed');
- const data = await resp.json();
- const reply = data.choices?.[0]?.message?.content || 'No response';
-
- appendMessage(reply, 'bot');
-
- chatHistory.push({ role: 'user', content: userText });
- chatHistory.push({ role: 'assistant', content: reply });
- } catch (e) {
- console.error(e);
- removeTypingIndicator();
- appendMessage('Unable to connect to the AI service. Please verify your HF_TOKEN configuration.', 'bot');
- }
- };
-
- sendBtn.addEventListener('click', sendMessage);
- input.addEventListener('keydown', (e) => {
- if (e.key === 'Enter') sendMessage();
- });
-})();
diff --git a/chatbot.js b/chatbot.js
index 2b1c157..2adec32 100644
--- a/chatbot.js
+++ b/chatbot.js
@@ -26,7 +26,7 @@
async function loadOrgData() {
try {
- const res = await fetch('github_summary.json');
+ const res = await fetch('/github_summary.json');
if (res.ok) {
orgData = await res.json();
}
@@ -141,8 +141,6 @@ RULES:
}
};
- const getHFToken = () => window.env?.HF_TOKEN || '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';
-
const sendMessage = async () => {
const userText = input.value.trim();
if (!userText) return;
@@ -150,18 +148,6 @@ RULES:
appendMessage(userText, 'user');
input.value = '';
- if (!window.envLoaded) {
- showTypingIndicator();
- await new Promise(resolve => window.addEventListener('envLoaded', resolve, { once: true }));
- removeTypingIndicator();
- }
-
- const token = getHFToken();
- if (!token) {
- appendMessage('API key missing. Please add HF_TOKEN to your .env file in the repository root to enable the AI assistant.', 'bot');
- return;
- }
-
showTypingIndicator();
const recentHistory = chatHistory.slice(-6).map(msg => ({
@@ -179,10 +165,9 @@ RULES:
};
try {
- const resp = await fetch('https://router.huggingface.co/v1/chat/completions', {
+ const resp = await fetch('/api/chat', {
method: 'POST',
headers: {
- Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
@@ -190,7 +175,7 @@ RULES:
removeTypingIndicator();
- if (!resp.ok) throw new Error('HuggingFace request failed');
+ if (!resp.ok) throw new Error('AI service request failed');
const data = await resp.json();
const reply = data.choices?.[0]?.message?.content || 'No response';
@@ -201,7 +186,7 @@ RULES:
} catch (e) {
console.error(e);
removeTypingIndicator();
- appendMessage('Unable to connect to the AI service. Please verify your HF_TOKEN configuration.', 'bot');
+ appendMessage('Unable to connect to the AI service. Please try again later.', 'bot');
}
};
diff --git a/dist/404err.html b/dist/404err.html
deleted file mode 100644
index 6b31f3c..0000000
--- a/dist/404err.html
+++ /dev/null
@@ -1,185 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 404err | Omnikon
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-warning
-System_Alert // CRITICAL
-
-
-
-
-
-
-
-
- ERR_404
-
-
- [ STATUS: NODE_OFFLINE ]
-
-
-
-
-
->
-Initializing diagnostic sequence... OK
-
-
->
-Locating requested vector... FAILED
-
-
- >
-
- PATH NOT FOUND. The requested node has been decommissioned or moved to a restricted sector.
-
-
-
->
-Awaiting user input_
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/dist/blogs.html b/dist/blogs.html
deleted file mode 100644
index abc38f5..0000000
--- a/dist/blogs.html
+++ /dev/null
@@ -1,780 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Developer Blog | Omnikon
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Loading article indexes...
-
-
-
-
- post_add
- No Articles Listed
-
- Be the first to share knowledge with the Omnikon community.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ADD NEW ARTICLE
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Parsing metadata server-side...
-
-
-
-
-
- info
- Please review extracted metadata before saving.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- warning
- Failed to perform action
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/dist/codeOfConduct.html b/dist/codeOfConduct.html
deleted file mode 100644
index e1938a7..0000000
--- a/dist/codeOfConduct.html
+++ /dev/null
@@ -1,274 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- codeOfConduct | Omnikon
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Code of Conduct
-
-
-update
- v2.1.0
-
-
-calendar_today
- Last Modified: 2024-03-15
-
-
-
-
-
-
-
-
-
-
-01.
- Our Pledge
-
-
- In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
-
-
- "We build systems, but we are a community of humans first."
-
-
-
-
-
-02.
- Our Standards
-
-Examples of behavior that contributes to creating a positive environment include:
-
--
-[+]
-Using welcoming and inclusive language.
-
--
-[+]
-Being respectful of differing viewpoints and experiences.
-
--
-[+]
-Gracefully accepting constructive criticism.
-
--
-[+]
-Focusing on what is best for the community.
-
--
-[+]
-Showing empathy towards other community members.
-
-
-Examples of unacceptable behavior by participants include:
-
--
-[-]
-The use of sexualized language or imagery and unwelcome sexual attention or advances.
-
--
-[-]
-Trolling, insulting/derogatory comments, and personal or political attacks.
-
--
-[-]
-Public or private harassment.
-
--
-[-]
-Publishing others' private information, such as a physical or electronic address, without explicit permission.
-
-
-
-
-
-
-03.
- Enforcement Responsibilities
-
-
- Community leaders are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
-
-
- Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
-
-
-
-
-Terminal
->_
-
-
- > $ contact --department=safety --priority=high
- Executing protocol...
- > If you experience or witness unacceptable behavior, report it immediately to:
- safety@omnikon.dev
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/dist/faqs.html b/dist/faqs.html
deleted file mode 100644
index 2e6659d..0000000
--- a/dist/faqs.html
+++ /dev/null
@@ -1,288 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- faqs | Omnikon
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-terminal
-sys/query/index.sh
-
-
-> FAQs
-
-
- ACCESSING KNOWLEDGE BASE... ESTABLISHING SECURE CONNECTION... READY.
- Consult the documented procedures below for operational clarity within the Omnikon framework.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Omnikon is an open-source community focused on empowering student developers through collaborative learning and building real-world projects. We aim to bridge the gap between academic learning and industry experience.
-
-
-
-
-
-
-
-
- Anyone with a passion for coding, learning, and collaborating! Whether you are a beginner taking your first steps in development or an experienced student looking to contribute to open-source projects, you are welcome here.
-
-
-
-
-
-
-
-
- You can start by joining our Discord community and checking out our GitHub repositories. Look for issues labeled "good first issue" or "help wanted", read our Contribution Guidelines, and don't hesitate to ask for help in the channels!
-
-
-
-
-
-
-
-
- Not at all! We encourage peer-to-peer learning. You can learn as you build by collaborating with other members and participating in community modules designed to help you upskill.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/dist/guidelines.html b/dist/guidelines.html
deleted file mode 100644
index a56e9a3..0000000
--- a/dist/guidelines.html
+++ /dev/null
@@ -1,285 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- guidelines | Omnikon
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- System Guidelines
-
-
-update
- v1.0.0
-
-
-calendar_today
- Last Modified: 2024-03-15
-
-
-
-
-
-
-
-
-Terminal
->_
-
-
-> $ ./execute read_protocols.sh
-Executing protocol...
-[INFO] Loading community constraints...
-[INFO] Parsing governance models...
-> [OK] System ready.
-
-
-
-
-
-
-
-
-01.
- Community Standards
-
-
-
-
-
- Respect the Override
-
- Harassment, discrimination, and toxic behavior are treated as fatal errors. Violators will be permanently permabanned from all cluster nodes. Constructive criticism is required; flaming is discarded.
-
-
-
-
-
- Knowledge Transfer
-
- Information hoarding is a deprecated practice. Open source implies open minds. Document your solutions, assist initiates, and elevate the collective intelligence of the network.
-
-
-
-
-
-
-
-
-
-
-02.
- Contribution Rules
-
-
--
-[::]
-
-Branch Naming Nomenclature
-Use type/scope/description. Example: feat/ui/cyber-terminal. Commits must follow Conventional Commits specification.
-
-
--
-[::]
-
-PR Pipeline Integrity
-All pull requests must pass automated CI/CD checks before human review. Ensure 90%+ test coverage on new modules. Draft PRs early to signal intent.
-
-
--
-[::]
-
-Style Constraints
-Strict adherence to the provided Tailwind theme configuration is mandatory. Arbitrary values (e.g., bg-[#123]) will trigger automatic rejection.
-
-
-
-
-
-
-
-
-
-
-03.
- Project Governance
-
-
- Decision-making operates on a modified consensus model. Core maintainers hold merge rights, but architectural shifts require an RFC (Request for Comments) period of at least 72 hours.
-
-
-
-
-
- Core Active
-
-
-
- RFCs Open: 3
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/dist/index.html b/dist/index.html
deleted file mode 100644
index d34a9a3..0000000
--- a/dist/index.html
+++ /dev/null
@@ -1,1633 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Omnikon | Open Source Community for Developers
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Building Omnikon OS...
-
-Open Source.
-Community Driven.
-Student Powered.
-
-
- We build developer centric open learning, and student developers to create real impact.
-
-
-
- bolt Omnikon Highlights
-
-
-- › AI-Learning Hub
-- › Real-world impact
-- › Open contribution
-- › Student-first
-- › GitHub-native
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-school
-
-
- Student
- Start your learning journey
-
-
-
-
-chevron_right
-
-
-menu_book
-
-
- Learner
- Learn, practice and build
-
-
-
-
-chevron_right
-
-
-code
-
-
- Builder
- Build projects & solve problems
-
-
-
-
-chevron_right
-
-
-groups
-
-
- Contributor
- Contribute to open source
-
-
-
-
-chevron_right
-
-
-military_tech
-
-
- Maintainer
- Lead projects & mentor
-
-
-
-
-
- Your journey. Your impact.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- OOO
- MMM
- NNN
- III
- KKK
- OOO
- NNN
-
-
-
-
-
-
-
-
-
-
- Ready to build the future together?
-
-
- Join thousands of developers & students open source and creating impact.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/dist/licence.html b/dist/licence.html
deleted file mode 100644
index 07236e6..0000000
--- a/dist/licence.html
+++ /dev/null
@@ -1,198 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- licence | Omnikon
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-terminal
-sys_license_viewer.exe
-
-
-
-
-
->
-cat LICENSE.txt
-
-
- MIT License
- Copyright (c) 2025 Omnikon Community
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-
-
->
-
-
-
-
-
-
-System Operational
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/fetch_community_feed.js b/fetch_community_feed.js
index fce8cee..255c396 100644
--- a/fetch_community_feed.js
+++ b/fetch_community_feed.js
@@ -7,8 +7,10 @@
await new Promise(resolve => window.addEventListener('envLoaded', resolve, { once: true }));
}
- const token = window.env?.GIT_OMNIKON_ALL || window.env?.GITHUB_TOKEN;
- container.innerHTML = '';
+ // No GitHub token is ever used in the browser. All authenticated org
+ // data is fetched server-side in CI (.github/workflows/update_projects.yml
+ // → public/github_summary.json). Anonymous REST calls are rate-limited
+ // to 60 req/hr per IP, which is sufficient for this feed.
const renderItem = (userLogin, userAvatar, title, timeStr, link) => {
const div = document.createElement('div');
@@ -72,72 +74,18 @@
return `${diffDays}d ago`;
};
- if (token) {
- try {
- const query = `
- query {
- repository(owner: "Omnikon-Org", name: "Website") {
- discussions(first: 8, orderBy: {field: CREATED_AT, direction: DESC}) {
- nodes {
- title
- url
- createdAt
- author {
- login
- avatarUrl
- }
- }
- }
- }
- }
- `;
- const resp = await fetch('https://api.github.com/graphql', {
- method: 'POST',
- headers: {
- Authorization: `bearer ${token}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ query })
- });
- if (resp.ok) {
- const result = await resp.json();
- const nodes = result.data?.repository?.discussions?.nodes;
- if (nodes && nodes.length > 0) {
- nodes.forEach(node => {
- renderItem(
- node.author?.login || 'anonymous',
- node.author?.avatarUrl,
- node.title,
- getTimeAgo(node.createdAt),
- node.url
- );
- });
- return;
- }
- }
- } catch (e) {
- console.warn('GraphQL query failed, falling back to REST issues', e);
- }
- }
-
let data;
try {
- const headers = token ? { Authorization: `token ${token}` } : {};
- let resp = await fetch('https://api.github.com/search/issues?q=org:Omnikon-Org+sort:created-desc', { headers });
- if (!resp.ok && resp.status === 401 && token) {
- console.warn('GitHub search issues returned 401 with token, retrying anonymously...');
- resp = await fetch('https://api.github.com/search/issues?q=org:Omnikon-Org+sort:created-desc');
- }
+ const resp = await fetch('https://api.github.com/search/issues?q=org:Omnikon-Org+sort:created-desc');
if (!resp.ok) throw new Error('GitHub search issues failed');
data = await resp.json();
} catch (e) {
- console.warn('Authenticated search failed, trying final anonymous request...', e);
- const resp = await fetch('https://api.github.com/search/issues?q=org:Omnikon-Org+sort:created-desc');
- if (!resp.ok) throw new Error('Anonymous backup search failed');
- data = await resp.json();
+ console.warn('Failed to load community feed:', e);
+ data = null;
}
- const items = data.items || [];
+ const items = (data && data.items) || [];
+ container.innerHTML = '';
if (items.length === 0) {
container.innerHTML = ' No discussions or activity found. ';
return;
diff --git a/fetch_recent_activity.js b/fetch_recent_activity.js
index a4f3e84..64f7ecc 100644
--- a/fetch_recent_activity.js
+++ b/fetch_recent_activity.js
@@ -7,23 +7,19 @@
await new Promise(resolve => window.addEventListener('envLoaded', resolve, { once: true }));
}
- const token = window.env?.GIT_OMNIKON_ALL || window.env?.GITHUB_TOKEN;
- let events;
+ // No GitHub token is ever used in the browser. All authenticated org
+ // data is fetched server-side in CI (.github/workflows/update_projects.yml
+ // → public/github_summary.json). Anonymous REST calls are rate-limited
+ // to 60 req/hr per IP, which is sufficient for this feed.
+ let events;
try {
- const headers = token ? { Authorization: `token ${token}` } : {};
- let resp = await fetch('https://api.github.com/orgs/Omnikon-Org/events', { headers });
- if (!resp.ok && resp.status === 401 && token) {
- console.warn('GitHub events request returned 401 with token, retrying anonymously...');
- resp = await fetch('https://api.github.com/orgs/Omnikon-Org/events');
- }
+ const resp = await fetch('https://api.github.com/orgs/Omnikon-Org/events');
if (!resp.ok) throw new Error('GitHub events request failed');
events = await resp.json();
} catch (e) {
- console.warn('Authenticated fetch failed, trying final anonymous request...', e);
- const resp = await fetch('https://api.github.com/orgs/Omnikon-Org/events');
- if (!resp.ok) throw new Error('Anonymous backup request failed');
- events = await resp.json();
+ console.warn('Failed to load recent activity:', e);
+ events = null;
}
container.innerHTML = '';
diff --git a/firebase.json b/firebase.json
new file mode 100644
index 0000000..0207c3f
--- /dev/null
+++ b/firebase.json
@@ -0,0 +1,5 @@
+{
+ "firestore": {
+ "rules": "firestore.rules"
+ }
+}
diff --git a/firestore.rules b/firestore.rules
index 6029048..f3e5f55 100644
--- a/firestore.rules
+++ b/firestore.rules
@@ -20,8 +20,8 @@ service cloud.firestore {
// USERS / AMBASSADORS COLLECTION
match /users/{userId} {
- // Authenticated users can read profiles for leaderboard and dashboard
- allow read: if isAuthenticated();
+ // Public access to read profiles for leaderboard and community listings
+ allow read: if true;
// Users can create their own profile, but cannot assign themselves 'admin' or set verified counts
allow create: if isOwner(userId)
diff --git a/generate-env.js b/generate-env.js
index 1c762b3..b20e0b8 100644
--- a/generate-env.js
+++ b/generate-env.js
@@ -1,6 +1,14 @@
const fs = require('fs');
const path = require('path');
+// Only PUBLIC-BY-DESIGN identifiers are emitted to the client.
+// These are required by the browser SDKs (Firebase auth, Supabase reads) and are
+// NOT secrets: Firebase web API keys and Supabase anon keys are public by design,
+// access control is enforced by Firestore rules / Supabase RLS.
+//
+// NEVER add secrets here (HF_TOKEN, ADMIN_SECRET, GIT_OMNIKON_ALL, ...).
+// Secrets live only in .env / Vercel env vars and are consumed server-side by
+// the Vercel functions in api/ (api/chat.js, api/blog-insert.js).
const envKeys = [
'FIREBASE_API_KEY',
'FIREBASE_AUTH_DOMAIN',
@@ -9,14 +17,12 @@ const envKeys = [
'FIREBASE_MESSAGING_SENDER_ID',
'FIREBASE_APP_ID',
'SUPABASE_URL',
- 'SUPABASE_ANON_KEY',
- 'HF_TOKEN',
- 'ADMIN_SECRET'
+ 'SUPABASE_ANON_KEY'
];
const envData = {};
-// Fallback to local .env if available (for local builds)
+// Read from local .env (for local builds)
try {
const envFile = fs.readFileSync(path.join(__dirname, '.env'), 'utf-8');
envFile.split('\n').forEach(line => {
@@ -24,7 +30,7 @@ try {
if (parts.length >= 2) {
const key = parts[0].trim();
const val = parts.slice(1).join('=').trim().replace(/^["']|["']$/g, '');
- if (envKeys.includes(key)) {
+ if (envKeys.includes(key) && val) {
envData[key] = val;
}
}
@@ -33,22 +39,10 @@ try {
// .env might not exist on Vercel, which is fine
}
-// Fallback default public keys for Firebase
-const defaultPublicKeys = {
- FIREBASE_API_KEY: 'AIzaSyBiNIObFcI06vECfiBivu967NLq0EbxNlg',
- FIREBASE_AUTH_DOMAIN: 'omnikon-web-auth-2026.firebaseapp.com',
- FIREBASE_PROJECT_ID: 'omnikon-web-auth-2026',
- FIREBASE_STORAGE_BUCKET: 'omnikon-web-auth-2026.firebasestorage.app',
- FIREBASE_MESSAGING_SENDER_ID: '1003258119714',
- FIREBASE_APP_ID: '1:1003258119714:web:e10c18f3955a9862242c7b'
-};
-
// Override with process.env (Vercel Environment Variables)
envKeys.forEach(key => {
if (process.env[key]) {
envData[key] = process.env[key];
- } else if (defaultPublicKeys[key]) {
- envData[key] = defaultPublicKeys[key];
}
});
@@ -59,4 +53,4 @@ if (!fs.existsSync(publicDir)) {
}
fs.writeFileSync(path.join(publicDir, 'env-public.json'), JSON.stringify(envData, null, 2));
-console.log('Successfully generated public/env-public.json from environment variables.');
+console.log('Generated public/env-public.json (public config only, no secrets).');
diff --git a/404err.html b/pages/404err.html
similarity index 90%
rename from 404err.html
rename to pages/404err.html
index 0628adb..fc8778f 100644
--- a/404err.html
+++ b/pages/404err.html
@@ -2,7 +2,7 @@
-
+
@@ -13,8 +13,8 @@
-
-
+
+
404err | Omnikon
@@ -39,21 +39,21 @@
-
-
+
+
-
+
-
+
-
-
@@ -171,8 +171,8 @@ |