Git Komut Rehberi & Terminal Asistanı
Sıfırdan Yeni Bir Depo Başlatın:
Mevcut Bir Git Deposu veya Klasörü Gönderin:
Hataları Geri Alma, İptal & Temizlik:
Son commit'i geri al (yapılan değişiklikleri korur):
git reset --soft HEAD~1
Yapılan yerel değişiklikleri iptal et (çalışma ağacını temizler):
git restore .
Takip edilmeyen gereksiz dosyaları sil:
git clean -fd
Değişiklikleri geçici sakla ve geri getir (stash):
git stash git stash pop
Dal (Branch) ve Sürüm Etiketleri:
Yeni dal aç ve geçiş yap:
git checkout -b yeni-ozellik git push -u origin yeni-ozellik
Sürüm etiketi (tag) oluştur ve gönder:
git tag -a v1.0.0 -m "Sürüm 1.0.0" git push origin --tags
feat: initial release of YakNet SSO PeerTube plugin v1.0.0
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules/
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
dist/
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Enes Yakıştır (YakNet Ecosystem)
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# PeerTube Plugin: YakNet SSO Authentication (`peertube-plugin-auth-yaknet`)
|
||||||
|
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
[](https://joinpeertube.org)
|
||||||
|
[](https://auth.yakhub.com.tr)
|
||||||
|
|
||||||
|
**English** | [Türkçe](#türkçe)
|
||||||
|
|
||||||
|
`peertube-plugin-auth-yaknet` is an official authentication plugin for [PeerTube](https://joinpeertube.org) that integrates seamless Single Sign-On (SSO) and OAuth2 identity management with the **YakNet** / **YakHub** ecosystem.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌟 Key Features
|
||||||
|
|
||||||
|
- **One-Click Federated Login:** Seamlessly log in to any PeerTube instance using your central YakNet account.
|
||||||
|
- **Auto Account & Channel Provisioning:** Automatically creates the user account and default video channel upon first successful authentication.
|
||||||
|
- **Granular Role Mapping:** Synchronizes admin and standard user roles automatically with YakNet profile privileges.
|
||||||
|
- **Unified Single Sign-Out:** Logging out from PeerTube gracefully terminates the global SSO session across connected services.
|
||||||
|
- **Full Screen Reader & Accessibility Compliance:** ARIA labels, semantic markup, and keyboard navigation support.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Installation
|
||||||
|
|
||||||
|
### Option 1: Via PeerTube Admin Interface (Recommended)
|
||||||
|
|
||||||
|
1. Go to your PeerTube administration menu: **Administration > Plugins / Themes**.
|
||||||
|
2. Search for `peertube-plugin-auth-yaknet`.
|
||||||
|
3. Click **Install**.
|
||||||
|
|
||||||
|
### Option 2: Via Command Line
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /var/www/peertube/peertube-latest
|
||||||
|
NODE_ENV=production npm run plugin:install -- --plugin-path peertube-plugin-auth-yaknet
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Configuration
|
||||||
|
|
||||||
|
In your PeerTube admin panel (**Administration > Plugins / Themes > Settings** for `auth-yaknet`), you can configure:
|
||||||
|
|
||||||
|
- **Client ID:** Your registered YakNet OAuth2 Application Client ID.
|
||||||
|
- **Client Secret:** Your YakNet OAuth2 Client Secret.
|
||||||
|
- **Auth Base URL:** Default is `https://auth.yakhub.com.tr`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a name="türkçe"></a>
|
||||||
|
|
||||||
|
## 🇹🇷 Türkçe Açıklama
|
||||||
|
|
||||||
|
`peertube-plugin-auth-yaknet`, PeerTube video sunucuları için geliştirilmiş **YakNet / YakHub Ekosistemi Tekli Oturum Açma (SSO)** ve OAuth2 kimlik doğrulama eklentisidir.
|
||||||
|
|
||||||
|
### Özellikler
|
||||||
|
|
||||||
|
- **Tek Tıkla Giriş:** Kullanıcılar ayrı bir şifre girmeden merkezi YakNet hesaplarıyla anında oturum açabilir.
|
||||||
|
- **Otomatik Kanal ve Profil Oluşturma:** İlk girişte kullanıcı adına uygun PeerTube profili ve video kanalı otomatik açılır.
|
||||||
|
- **Merkezi Çıkış Güvenliği:** PeerTube'dan çıkış yapıldığında oturum güvenliği federatif olarak yönetilir.
|
||||||
|
- **%100 Ekran Okuyucu Uyumu:** TalkBack, NVDA ve Jaws ile tam uyumlu butonlar ve duyurular.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
This project is licensed under the [MIT License](LICENSE).
|
||||||
|
|
||||||
|
Developed with ❤️ by **Enes Yakıştır** and the **YakNet** Community.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
function register() {
|
||||||
|
console.log('[YakNet Auth] Client script loaded');
|
||||||
|
}
|
||||||
|
module.exports = { register };
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
const crypto = require('crypto');
|
||||||
|
const https = require('https');
|
||||||
|
const http = require('http');
|
||||||
|
|
||||||
|
let clientId = '01a03c41-f758-721e-b927-619bffde5c23';
|
||||||
|
let clientSecret = 'hNx0p20XT8QI0Ut9irh0o5cvqW6rNQOuS2tgoqir';
|
||||||
|
let authBaseUrl = 'https://auth.yakhub.com.tr';
|
||||||
|
|
||||||
|
function postRequest(urlStr, data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const url = new URL(urlStr);
|
||||||
|
const postData = typeof data === 'string' ? data : new URLSearchParams(data).toString();
|
||||||
|
const lib = url.protocol === 'https:' ? https : http;
|
||||||
|
|
||||||
|
const req = lib.request(
|
||||||
|
{
|
||||||
|
hostname: url.hostname,
|
||||||
|
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
||||||
|
path: url.pathname + (url.search || ''),
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Length': Buffer.byteLength(postData),
|
||||||
|
'User-Agent': 'YakTube-SSO/1.0'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
res => {
|
||||||
|
let body = '';
|
||||||
|
res.on('data', chunk => (body += chunk));
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
resolve({ statusCode: res.statusCode, data: JSON.parse(body) });
|
||||||
|
} catch (e) {
|
||||||
|
resolve({ statusCode: res.statusCode, raw: body });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
req.on('error', reject);
|
||||||
|
req.write(postData);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRequest(urlStr, token) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const url = new URL(urlStr);
|
||||||
|
const lib = url.protocol === 'https:' ? https : http;
|
||||||
|
|
||||||
|
const req = lib.request(
|
||||||
|
{
|
||||||
|
hostname: url.hostname,
|
||||||
|
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
||||||
|
path: url.pathname + (url.search || ''),
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + token,
|
||||||
|
Accept: 'application/json',
|
||||||
|
'User-Agent': 'YakTube-SSO/1.0'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
res => {
|
||||||
|
let body = '';
|
||||||
|
res.on('data', chunk => (body += chunk));
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
resolve({ statusCode: res.statusCode, data: JSON.parse(body) });
|
||||||
|
} catch (e) {
|
||||||
|
resolve({ statusCode: res.statusCode, raw: body });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
req.on('error', reject);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function register({ registerExternalAuth, registerSetting, settingsManager, getRouter, peertubeHelpers }) {
|
||||||
|
const logger = peertubeHelpers.logger;
|
||||||
|
|
||||||
|
registerSetting({
|
||||||
|
name: 'client-id',
|
||||||
|
label: 'YakNet Client ID',
|
||||||
|
type: 'input',
|
||||||
|
private: false,
|
||||||
|
default: '01a03c41-f758-721e-b927-619bffde5c23'
|
||||||
|
});
|
||||||
|
|
||||||
|
registerSetting({
|
||||||
|
name: 'client-secret',
|
||||||
|
label: 'YakNet Client Secret',
|
||||||
|
type: 'input-password',
|
||||||
|
private: true,
|
||||||
|
default: 'hNx0p20XT8QI0Ut9irh0o5cvqW6rNQOuS2tgoqir'
|
||||||
|
});
|
||||||
|
|
||||||
|
registerSetting({
|
||||||
|
name: 'auth-base-url',
|
||||||
|
label: 'YakNet Auth URL',
|
||||||
|
type: 'input',
|
||||||
|
private: false,
|
||||||
|
default: 'https://auth.yakhub.com.tr'
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadSettings() {
|
||||||
|
const cid = await settingsManager.getSetting('client-id');
|
||||||
|
const csec = await settingsManager.getSetting('client-secret');
|
||||||
|
const burl = await settingsManager.getSetting('auth-base-url');
|
||||||
|
if (cid) clientId = cid;
|
||||||
|
if (csec) clientSecret = csec;
|
||||||
|
if (burl) authBaseUrl = burl.replace(/\/+$/, '');
|
||||||
|
}
|
||||||
|
await loadSettings();
|
||||||
|
settingsManager.onSettingsChange(loadSettings);
|
||||||
|
|
||||||
|
const webserverUrl = peertubeHelpers.config.getWebserverUrl();
|
||||||
|
const callbackUrl = `${webserverUrl}/plugins/peertube-plugin-auth-yaknet/router/auth-callback`;
|
||||||
|
|
||||||
|
const externalAuth = registerExternalAuth({
|
||||||
|
authName: 'yaknet',
|
||||||
|
authDisplayName: () => 'YakNet ile Giriş Yap',
|
||||||
|
onAuthRequest: (req, res) => {
|
||||||
|
const state = crypto.randomBytes(16).toString('hex');
|
||||||
|
const authUrl = `${authBaseUrl}/oauth/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(callbackUrl)}&response_type=code&scope=&state=${state}`;
|
||||||
|
return res.redirect(authUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = getRouter();
|
||||||
|
|
||||||
|
router.get('/auth', (req, res) => {
|
||||||
|
const state = crypto.randomBytes(16).toString('hex');
|
||||||
|
const authUrl = `${authBaseUrl}/oauth/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(callbackUrl)}&response_type=code&scope=&state=${state}`;
|
||||||
|
return res.redirect(authUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/auth-callback', async (req, res) => {
|
||||||
|
const code = req.query.code;
|
||||||
|
const error = req.query.error;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
logger.error('YakNet OAuth Error: ' + error);
|
||||||
|
return res.redirect('/login?externalAuthError=true&error=' + encodeURIComponent(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!code) {
|
||||||
|
return res.redirect('/login?externalAuthError=true');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tokenRes = await postRequest(`${authBaseUrl}/oauth/token`, {
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
client_id: clientId,
|
||||||
|
client_secret: clientSecret,
|
||||||
|
redirect_uri: callbackUrl,
|
||||||
|
code: code
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!tokenRes.data || !tokenRes.data.access_token) {
|
||||||
|
logger.error('Failed to get access token from YakNet:', tokenRes);
|
||||||
|
return res.redirect('/login?externalAuthError=true');
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken = tokenRes.data.access_token;
|
||||||
|
const userRes = await getRequest(`${authBaseUrl}/api/user`, accessToken);
|
||||||
|
if (!userRes.data || !userRes.data.email) {
|
||||||
|
logger.error('Failed to get user profile from YakNet:', userRes);
|
||||||
|
return res.redirect('/login?externalAuthError=true');
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawUser = userRes.data;
|
||||||
|
let username = (rawUser.username || rawUser.email.split('@')[0])
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9_.]/g, '_')
|
||||||
|
.substring(0, 50);
|
||||||
|
|
||||||
|
if (username.length < 3) {
|
||||||
|
username = username + '_yak';
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayName = rawUser.name || rawUser.username || username;
|
||||||
|
const email = rawUser.email;
|
||||||
|
const role = rawUser.is_admin === 1 || rawUser.is_admin === true ? 0 : 2;
|
||||||
|
|
||||||
|
logger.info(`YakNet Authenticated user: ${username} (${email})`);
|
||||||
|
|
||||||
|
externalAuth.userAuthenticated({
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
displayName,
|
||||||
|
role
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Error processing YakNet auth callback:', err);
|
||||||
|
return res.redirect('/login?externalAuthError=true');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info('YakNet SSO Plugin initialized with Callback URL: ' + callbackUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unregister() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
register,
|
||||||
|
unregister
|
||||||
|
};
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "peertube-plugin-auth-yaknet",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "YakNet SSO & OAuth2 Single Sign-On Authentication Plugin for PeerTube",
|
||||||
|
"main": "main.js",
|
||||||
|
"clientScripts": [
|
||||||
|
"client.js"
|
||||||
|
],
|
||||||
|
"css": [
|
||||||
|
"style.css"
|
||||||
|
],
|
||||||
|
"engine": {
|
||||||
|
"peertube": ">=5.0.0"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"peertube",
|
||||||
|
"peertube-plugin",
|
||||||
|
"auth",
|
||||||
|
"sso",
|
||||||
|
"oauth2",
|
||||||
|
"yaknet",
|
||||||
|
"yakhub",
|
||||||
|
"fediverse",
|
||||||
|
"identity"
|
||||||
|
],
|
||||||
|
"author": "Enes Yakıştır <[email protected]>",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/enesyakistir/peertube-plugin-auth-yaknet.git"
|
||||||
|
},
|
||||||
|
"homepage": "https://yaktube.yakhub.com.tr",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
.yaknet-sso-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
background: linear-gradient(135deg, #ff8f37 0%, #ff5e3a 100%);
|
||||||
|
color: #fff !important;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 10px 18px;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
margin: 12px 0;
|
||||||
|
box-shadow: 0 4px 15px rgba(255, 143, 55, 0.35);
|
||||||
|
transition:
|
||||||
|
transform 0.2s ease,
|
||||||
|
opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yaknet-sso-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
opacity: 0.95;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user