@xuda.io/ai_module 1.1.5632 → 1.1.5634
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.mjs +75 -1
- package/index_ms.mjs +4 -0
- package/index_msa.mjs +4 -0
- package/package.json +5 -2
package/index.mjs
CHANGED
|
@@ -8638,6 +8638,63 @@ export const create_avatar = async function (req, job_id, headers) {
|
|
|
8638
8638
|
}
|
|
8639
8639
|
};
|
|
8640
8640
|
|
|
8641
|
+
// generate_login_screen: paid-plan alternative to uploading — AI-generate a
|
|
8642
|
+
// full-bleed landscape login background from a text prompt. Credit-gated like
|
|
8643
|
+
// avatar generation (create_image records usage). The PNG render is re-encoded to
|
|
8644
|
+
// WebP to satisfy the 5 MB spec, then handed to the shared drive writer over the
|
|
8645
|
+
// drive_module queue so it is persisted + served publicly exactly like an upload.
|
|
8646
|
+
export const generate_login_screen = async function (req, job_id, headers = {}) {
|
|
8647
|
+
const { uid, prompt } = req;
|
|
8648
|
+
const tempDir = os.tmpdir();
|
|
8649
|
+
const uniqueId = Date.now() + '_' + Math.random().toString(36).substring(7);
|
|
8650
|
+
const tempOutputPath = path.join(tempDir, `login_screen_${uniqueId}.webp`);
|
|
8651
|
+
try {
|
|
8652
|
+
if (!uid) throw new Error('not authorized (no uid)');
|
|
8653
|
+
if (!prompt || typeof prompt !== 'string' || !prompt.trim()) throw new Error('a prompt is required');
|
|
8654
|
+
|
|
8655
|
+
const account_profile_info = await get_active_account_profile_info(uid);
|
|
8656
|
+
|
|
8657
|
+
// credits: validate_credits_limit RETURNS an Error object when over the hard limit
|
|
8658
|
+
const credit_err = await validate_credits_limit(uid);
|
|
8659
|
+
if (credit_err) return { code: -25, data: 'AI credits reached the hard limit' };
|
|
8660
|
+
|
|
8661
|
+
const wrapped_prompt = `Create a full-bleed, decorative LANDSCAPE background image for a login screen. Subject/style: ${prompt.trim()}. It must contain no text, letters, logos, watermarks, UI elements, or human faces; use a cohesive, calm color palette with soft contrast so a login form stays readable on top. Fill the whole frame edge to edge.`;
|
|
8662
|
+
|
|
8663
|
+
// gpt-image landscape render; quality 'low' is fine for a decorative background.
|
|
8664
|
+
const base64 = await create_image(uid, wrapped_prompt, 'gpt-image-1-mini', '1536x1024', 1, 1536, 1024, { purpose: 'login_screen' }, account_profile_info, 'low');
|
|
8665
|
+
if (!base64) throw new Error('image generation failed');
|
|
8666
|
+
|
|
8667
|
+
// create_image emits PNG; re-encode to WebP to stay well under the 5 MB spec.
|
|
8668
|
+
const webp = await sharp(Buffer.from(base64, 'base64')).webp({ quality: 82 }).toBuffer();
|
|
8669
|
+
await fs.promises.writeFile(tempOutputPath, webp);
|
|
8670
|
+
|
|
8671
|
+
const file_obj = { originalname: 'login_screen.webp', path: tempOutputPath, mimetype: 'image/webp' };
|
|
8672
|
+
const store_ret = await drive_ms.store_login_screen_from_path(uid, file_obj, job_id, headers);
|
|
8673
|
+
if (store_ret.code !== 1) return store_ret;
|
|
8674
|
+
|
|
8675
|
+
const o = store_ret.data.login_screen_obj;
|
|
8676
|
+
return {
|
|
8677
|
+
code: 1,
|
|
8678
|
+
data: {
|
|
8679
|
+
login_screen: {
|
|
8680
|
+
source: 'personal',
|
|
8681
|
+
media_type: 'image',
|
|
8682
|
+
file_url: o.file_url,
|
|
8683
|
+
w: o.w,
|
|
8684
|
+
h: o.h,
|
|
8685
|
+
bytes: o.bytes,
|
|
8686
|
+
uploaded_ts: o.uploaded_ts,
|
|
8687
|
+
},
|
|
8688
|
+
},
|
|
8689
|
+
};
|
|
8690
|
+
} catch (err) {
|
|
8691
|
+
try {
|
|
8692
|
+
await fs.promises.unlink(tempOutputPath);
|
|
8693
|
+
} catch (e) {}
|
|
8694
|
+
return { code: -25, data: err.message };
|
|
8695
|
+
}
|
|
8696
|
+
};
|
|
8697
|
+
|
|
8641
8698
|
export const get_profile_avatar = async function (profile_picture, uid, prompt, account_profile_info, account_type, docType = 'account', _id, metadata = {}, business_size = 'unknown', name, email, job_id, headers) {
|
|
8642
8699
|
const tempDir = os.tmpdir();
|
|
8643
8700
|
const uniqueId = Date.now() + '_' + Math.random().toString(36).substring(7);
|
|
@@ -14937,13 +14994,26 @@ const _widget_verify_google_id_token = async function (id_token) {
|
|
|
14937
14994
|
return payload;
|
|
14938
14995
|
};
|
|
14939
14996
|
|
|
14997
|
+
// Widget/site visitors never hit the dashboard login, which is what normally
|
|
14998
|
+
// runs misc.login_maintenance_fix (plans, Stripe customer, starter credits).
|
|
14999
|
+
// Run it from here instead, once per account: gate on stripe_customer_id since
|
|
15000
|
+
// create_stripe_customer is the step that stamps membership_plan. Fire-and-forget
|
|
15001
|
+
// so signup latency is unaffected; without token_ret the account_project_id fix
|
|
15002
|
+
// throws by design AFTER the plan/credit fixes have completed.
|
|
15003
|
+
const _widget_account_maintenance = function (uid) {
|
|
15004
|
+
try {
|
|
15005
|
+
misc_msa.login_maintenance_fix(uid, null);
|
|
15006
|
+
} catch (err) {}
|
|
15007
|
+
};
|
|
15008
|
+
|
|
14940
15009
|
const _widget_find_or_create_visitor = async function (email, name, google_sub, picture) {
|
|
14941
15010
|
const ret = await db_module.find_couch_query('xuda_accounts', {
|
|
14942
15011
|
selector: { 'account_info.email': email },
|
|
14943
|
-
fields: ['_id'],
|
|
15012
|
+
fields: ['_id', 'stripe_customer_id'],
|
|
14944
15013
|
limit: 1,
|
|
14945
15014
|
});
|
|
14946
15015
|
if (ret.docs && ret.docs.length) {
|
|
15016
|
+
if (!ret.docs[0].stripe_customer_id) _widget_account_maintenance(ret.docs[0]._id);
|
|
14947
15017
|
// If we have a google_sub and the existing account lacks one, attach it.
|
|
14948
15018
|
// Also seed the Google photo as the profile_picture when the account has no
|
|
14949
15019
|
// avatar yet, so avatar generation has a source to work from.
|
|
@@ -14987,6 +15057,9 @@ const _widget_find_or_create_visitor = async function (email, name, google_sub,
|
|
|
14987
15057
|
}
|
|
14988
15058
|
const doc = {
|
|
14989
15059
|
_id: await _common.xuda_get_uuid('account'),
|
|
15060
|
+
// 1 = unverified (Boaz 2026-07-06): widget signups stay plain unverified
|
|
15061
|
+
// until a real dashboard login; keeps them out of search_users (stat 3)
|
|
15062
|
+
// and out of the missing-plan report (stat-3-only).
|
|
14990
15063
|
stat: 1,
|
|
14991
15064
|
docType: 'account',
|
|
14992
15065
|
source: 'widget',
|
|
@@ -15000,6 +15073,7 @@ const _widget_find_or_create_visitor = async function (email, name, google_sub,
|
|
|
15000
15073
|
}
|
|
15001
15074
|
const save_ret = await db_module.save_couch_doc('xuda_accounts', doc);
|
|
15002
15075
|
if (save_ret.code < 0) throw new Error(save_ret.data || 'visitor_create_failed');
|
|
15076
|
+
_widget_account_maintenance(doc._id);
|
|
15003
15077
|
return doc._id;
|
|
15004
15078
|
};
|
|
15005
15079
|
|
package/index_ms.mjs
CHANGED
|
@@ -157,6 +157,10 @@ export const create_avatar = async function (...args) {
|
|
|
157
157
|
return await broker.send_to_queue("create_avatar", ...args);
|
|
158
158
|
};
|
|
159
159
|
|
|
160
|
+
export const generate_login_screen = async function (...args) {
|
|
161
|
+
return await broker.send_to_queue("generate_login_screen", ...args);
|
|
162
|
+
};
|
|
163
|
+
|
|
160
164
|
export const get_profile_avatar = async function (...args) {
|
|
161
165
|
return await broker.send_to_queue("get_profile_avatar", ...args);
|
|
162
166
|
};
|
package/index_msa.mjs
CHANGED
|
@@ -157,6 +157,10 @@ export const create_avatar = function (...args) {
|
|
|
157
157
|
broker.send_to_queue_async("create_avatar", ...args);
|
|
158
158
|
};
|
|
159
159
|
|
|
160
|
+
export const generate_login_screen = function (...args) {
|
|
161
|
+
broker.send_to_queue_async("generate_login_screen", ...args);
|
|
162
|
+
};
|
|
163
|
+
|
|
160
164
|
export const get_profile_avatar = function (...args) {
|
|
161
165
|
broker.send_to_queue_async("get_profile_avatar", ...args);
|
|
162
166
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xuda.io/ai_module",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5634",
|
|
4
4
|
"description": "Xuda AI Module",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -31,5 +31,8 @@
|
|
|
31
31
|
"test:codex": "node tests/execute-codex-request.mjs"
|
|
32
32
|
},
|
|
33
33
|
"author": "Xuda Llc",
|
|
34
|
-
"license": "ISC"
|
|
34
|
+
"license": "ISC",
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "restricted"
|
|
37
|
+
}
|
|
35
38
|
}
|