feat: AYON single-sign-on (ticket exchange, task boards, browser entry)
- POST /api/auth/ayon/exchange: redeem single-use ticket (issued by the AYON addon) via AYON_EXCHANGE_URL, mint session JWT, get-or-create internal user row and the <project>/<task> board in the AYON collection - db: getOrCreateAyonUser / getOrCreateAyonBoard / grantAyonCollectionAccess - frontend: /b route redeems ticket from URL and forwards to the board - password login/register paths untouched (legacy instance support)
This commit is contained in:
+82
-3
@@ -429,6 +429,10 @@ function createCollection({ id, name, description, createdBy }) {
|
||||
return getCollection(id);
|
||||
}
|
||||
|
||||
function getCollectionByName(name) {
|
||||
return db.prepare('SELECT * FROM collections WHERE name = ?').get(name);
|
||||
}
|
||||
|
||||
function updateCollection(collectionId, { name, description, isPublic, shareToken }) {
|
||||
const fields = [];
|
||||
const params = [];
|
||||
@@ -816,13 +820,88 @@ async function seedAdminFromEnv() {
|
||||
console.log(`[db] Seeded admin user: ${email} (username: ${finalUsername})`);
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
// AYON integration helpers
|
||||
// ---------------------
|
||||
|
||||
// Collection that holds all AYON task boards.
|
||||
const AYON_COLLECTION_NAME = 'AYON';
|
||||
|
||||
/**
|
||||
* Get-or-create the internal user row backing an AYON identity.
|
||||
* AYON users never log in here — the row only exists so that boards,
|
||||
* images, threads and comments (which reference users.id) keep working.
|
||||
* Password hash is an unusable sentinel.
|
||||
*/
|
||||
function getOrCreateAyonUser(username, displayName) {
|
||||
const existing = getUserByUsername(username);
|
||||
if (existing) return existing;
|
||||
|
||||
const email = `${username}@ayon.local`;
|
||||
const byEmail = getUserByEmail(email);
|
||||
if (byEmail) return byEmail;
|
||||
|
||||
return createUser({
|
||||
id: uuidv4(),
|
||||
email,
|
||||
username,
|
||||
passwordHash: `!ayon:${uuidv4()}`, // no password ever matches this
|
||||
displayName: displayName || username,
|
||||
role: 'member',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get-or-create the board for an AYON task: board name is
|
||||
* `<project>/<task_path>`, living in the shared "AYON" collection.
|
||||
* Returns { board, collection, created }.
|
||||
*/
|
||||
function getOrCreateAyonBoard(project, taskPath) {
|
||||
const name = `${project}/${taskPath}`;
|
||||
|
||||
let collection = getCollectionByName(AYON_COLLECTION_NAME);
|
||||
if (!collection) {
|
||||
// Bootstrap user owns the container collection
|
||||
const owner = getOrCreateAyonUser('ayon-system', 'AYON System');
|
||||
collection = createCollection({
|
||||
id: uuidv4(),
|
||||
name: AYON_COLLECTION_NAME,
|
||||
description: 'Task boards created by the AYON integration',
|
||||
createdBy: owner.id,
|
||||
});
|
||||
addCollectionMember(collection.id, owner.id, 'owner');
|
||||
}
|
||||
const ownerId = collection.created_by;
|
||||
|
||||
const existing = db.prepare(
|
||||
'SELECT * FROM boards WHERE collection_id = ? AND name = ?'
|
||||
).get(collection.id, name);
|
||||
if (existing) return { board: existing, collection, created: false };
|
||||
|
||||
const board = createBoard({
|
||||
id: uuidv4(),
|
||||
collectionId: collection.id,
|
||||
name,
|
||||
description: `AYON task board (${name})`,
|
||||
createdBy: ownerId,
|
||||
});
|
||||
return { board, collection, created: true };
|
||||
}
|
||||
|
||||
/** Grant a user editor membership on the AYON collection (idempotent). */
|
||||
function grantAyonCollectionAccess(userId) {
|
||||
const collection = getCollectionByName(AYON_COLLECTION_NAME);
|
||||
if (!collection) return;
|
||||
const member = getCollectionMember(collection.id, userId);
|
||||
if (!member) addCollectionMember(collection.id, userId, 'editor');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
db,
|
||||
// AYON integration
|
||||
getOrCreateAyonUser, getOrCreateAyonBoard, grantAyonCollectionAccess,
|
||||
// Users
|
||||
getUserByEmail, getUserById, getUserByUsername,
|
||||
createUser,
|
||||
getAllUsers, updateUserPassword, deactivateUser, getUserCount,
|
||||
// Collections
|
||||
getCollections, getCollection, getCollectionByShareToken,
|
||||
createCollection, updateCollection, deleteCollection,
|
||||
getCollectionMembers, getCollectionMember, addCollectionMember, removeCollectionMember,
|
||||
|
||||
Reference in New Issue
Block a user