- Rename .env.staging → .env.dev (and .default template) - Add .env.test.default for the test tier (test-api.oneskyit.com) - build:staging → build:dev/test/prod using vite --mode <name> - deploy:staging → deploy:dev; add deploy:test - Dockerfile: ARG BUILD_MODE=dev; explicit .env.runtime copy per mode - .dockerignore: rewritten (deduped); allow .env.dev/.env.test/.env.prod - .gitignore: track .env.dev.default and .env.test.default - Remove dead PUBLIC_AE_* imports from ae_stores.ts (ACCOUNT_ID, EVENT_ID, NO_ACCOUNT_ID_TOKEN, SPONSORSHIP_CFG_ID); sponsorship_cfg_id defaults to null - Strip dead vars from .env.prod.default template (AE_CFG_ID, AE_APP_NODE_PORT, ACCOUNT_ID, EVENT_ID, SPONSORSHIP_CFG_ID, NO_ACCOUNT_ID_TOKEN) - GUIDE__Development.md: build:staging → build:dev Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
387 lines
12 KiB
TypeScript
387 lines
12 KiB
TypeScript
// store_versions MUST be first import — its side-effect wipes stale localStorage
|
|
// before svelte-persisted-store hydrates from it.
|
|
import { AE_LOC_VERSION } from '$lib/stores/store_versions';
|
|
|
|
import { auth_loc_defaults } from '$lib/stores/ae_stores__auth_loc_defaults';
|
|
|
|
import { persisted } from 'svelte-persisted-store';
|
|
|
|
import { readable, writable } from 'svelte/store';
|
|
import type { Readable, Writable } from 'svelte/store';
|
|
|
|
import {
|
|
PUBLIC_AE_API_PROTOCOL,
|
|
PUBLIC_AE_API_SERVER,
|
|
PUBLIC_AE_API_BAK_SERVER,
|
|
PUBLIC_AE_API_PORT,
|
|
PUBLIC_AE_API_PATH,
|
|
PUBLIC_AE_API_SECRET_KEY,
|
|
PUBLIC_AE_API_CRUD_SUPER_KEY,
|
|
PUBLIC_AE_NO_ACCOUNT_ID,
|
|
// PUBLIC_AE_NO_ACCOUNT_ID_TOKEN,
|
|
// PUBLIC_AE_ACCOUNT_ID,
|
|
// PUBLIC_AE_EVENT_ID,
|
|
// PUBLIC_AE_SPONSORSHIP_CFG_ID
|
|
} from '$env/static/public';
|
|
|
|
const api_server_fqdn = PUBLIC_AE_API_SERVER; // 'api.oneskyit.com'
|
|
const api_base_url = `${PUBLIC_AE_API_PROTOCOL}://${PUBLIC_AE_API_SERVER}:${PUBLIC_AE_API_PORT}${PUBLIC_AE_API_PATH}`;
|
|
const api_base_url_bak = `${PUBLIC_AE_API_PROTOCOL}://${PUBLIC_AE_API_BAK_SERVER}:${PUBLIC_AE_API_PORT}${PUBLIC_AE_API_PATH}`;
|
|
|
|
const api_secret_key = PUBLIC_AE_API_SECRET_KEY;
|
|
const api_crud_super_key = PUBLIC_AE_API_CRUD_SUPER_KEY;
|
|
|
|
// const ae_account_id = PUBLIC_AE_ACCOUNT_ID;
|
|
const ae_account_id: null | string = null;
|
|
const ae_no_account_id = PUBLIC_AE_NO_ACCOUNT_ID;
|
|
// const ae_no_account_id_token = PUBLIC_AE_NO_ACCOUNT_ID_TOKEN;
|
|
// const ae_event_id = PUBLIC_AE_EVENT_ID;
|
|
// const ae_sponsorship_cfg_id = PUBLIC_AE_SPONSORSHIP_CFG_ID;
|
|
|
|
// Loose bag-of-anything type used throughout the store. Once stores are broken into
|
|
// typed domain files, individual structs will have proper interfaces instead.
|
|
export type key_val = {
|
|
[key: string]: any;
|
|
};
|
|
|
|
import { string_snippets } from '$lib/utils/ae_string_snippets';
|
|
export const ae_snip = string_snippets;
|
|
|
|
// Deployment version stamp. Compared against ae_sess.ver in +layout.svelte to detect
|
|
// stale persisted data after a deploy (triggers a reload). Bump this alongside
|
|
// ae_app_session_data_defaults.ver whenever a deploy changes expected stored shape.
|
|
// See store_versions.ts for the schema-level localStorage wipe mechanism (separate concern).
|
|
const ver = '2025-05-01_1445';
|
|
|
|
// *** BEGIN *** Longer-term app data. This should be stored to local storage.
|
|
const ae_app_local_data_defaults: key_val = {
|
|
__version: AE_LOC_VERSION, // Schema version gate — see store_versions.ts
|
|
last_page_reload: null,
|
|
last_cache_refresh: Date.now(),
|
|
cache_expired: false,
|
|
// Deployment stamp — compared against ae_sess.ver in +layout.svelte to trigger
|
|
// a reload when the persisted local data predates the current deploy.
|
|
ver: ver,
|
|
|
|
name: 'Aether - App Hub',
|
|
|
|
theme: 'light',
|
|
theme_mode: 'light',
|
|
theme_name: 'AE_Firefly', // OSIT default theme
|
|
font_size_mode: 'default', // 'default' | 'larger' | 'smaller'
|
|
iframe: false,
|
|
|
|
browser_type: null, // Safari needs special handling for scrolling in iframes.
|
|
|
|
title: `OSIT's Æ`,
|
|
|
|
debug_mode: false,
|
|
edit_mode: false,
|
|
adv_mode: false,
|
|
sync_local_config: true, // When true, pulls site config from the remote API on load.
|
|
|
|
// Auth and identity section — see ae_stores__auth_loc_defaults.ts.
|
|
...auth_loc_defaults,
|
|
|
|
qry__enabled: 'enabled', // all, disabled, enabled
|
|
qry__hidden: 'not_hidden', // all, hidden, not_hidden
|
|
qry__limit: 20,
|
|
qry__offset: 0,
|
|
|
|
qr_scanner_version: 'one',
|
|
|
|
admin: {
|
|
show_element__sql_qry: false,
|
|
show_element__sql_qry_results: false
|
|
},
|
|
|
|
sys_menu: {
|
|
hide: false,
|
|
expand: false,
|
|
hide_access_type: false,
|
|
expand_access_type: false,
|
|
hide_edit_mode: false,
|
|
expand_edit_mode: false,
|
|
hide_user: false,
|
|
expand_user: false,
|
|
hide_theme: false,
|
|
expand_theme: false,
|
|
hide_app_cfg: false,
|
|
expand_app_cfg: false
|
|
},
|
|
debug_menu: {
|
|
hide: false,
|
|
expand: false
|
|
},
|
|
|
|
app_cfg: {
|
|
show_element__header: false,
|
|
show_element__footer: false,
|
|
show_element__menu: false,
|
|
show_element__menu_btn: true,
|
|
|
|
show_element__access_type: true,
|
|
show_element__passcode_input: true,
|
|
show_element__cfg: true,
|
|
show_element__cfg_detail: false,
|
|
show_element__sign_in_out: true, // Show the sign-in/out button in the UI
|
|
show_opt__debug: true,
|
|
show_opt__permissions: true,
|
|
show_opt__reset: true,
|
|
show_opt__sync: true,
|
|
show_opt__theme: true,
|
|
show_opt__utilities: true
|
|
},
|
|
|
|
files: {
|
|
processed_file_kv: {},
|
|
uploaded_file_kv: {},
|
|
video_clip_file_kv: {},
|
|
add_to_use_files_method: 'upload' // upload, select
|
|
},
|
|
|
|
ds: {},
|
|
hub: {
|
|
show_element__cfg: true,
|
|
show_element__cfg_detail: false,
|
|
show_element__access_type: true,
|
|
theme_mode: 'light',
|
|
theme_name: 'wintry', // wintry
|
|
|
|
classes__form:
|
|
'border border-surface-200 p-4 space-y-4 rounded-container',
|
|
|
|
qr: {}
|
|
},
|
|
mod: {
|
|
archives: {},
|
|
|
|
events: {
|
|
event_id: null,
|
|
|
|
show_edit__event_presenter_obj: false,
|
|
show_list__event_presenter_obj_li: true,
|
|
show_view__event_presenter_obj: false,
|
|
|
|
submit_status: null,
|
|
|
|
// When set, new presenters are automatically assigned to this session.
|
|
default_session_id: null
|
|
},
|
|
|
|
journals: {},
|
|
|
|
posts: {},
|
|
|
|
sponsorships: {
|
|
cfg_id: null,
|
|
|
|
for_type: null,
|
|
for_id: null,
|
|
|
|
// Max complimentary guests per sponsorship tier (0 = no tier).
|
|
// These are fallback defaults; the active event config should override via events_cfg_json.
|
|
level_guest_max_li: {
|
|
0: 0,
|
|
1: 4,
|
|
2: 8,
|
|
3: 8,
|
|
4: 8,
|
|
5: 8,
|
|
6: 16,
|
|
7: 16
|
|
},
|
|
|
|
show_edit__sponsorship_obj: false,
|
|
show_list__sponsorship_obj_li: true,
|
|
show_view__sponsorship_obj: false,
|
|
|
|
show_question__accommodations: false,
|
|
|
|
submit_status: null // 'saving', 'created', 'updated'
|
|
}
|
|
|
|
// testing: {},
|
|
}
|
|
};
|
|
|
|
export const ae_loc: Writable<key_val> = persisted(
|
|
'ae_loc',
|
|
ae_app_local_data_defaults
|
|
);
|
|
|
|
// *** BEGIN *** Temporary app data. This should be stored to session storage.
|
|
const ae_app_session_data_defaults: key_val = {
|
|
// Deployment stamp — compared against ae_loc.ver in +layout.svelte.
|
|
// When these differ, the app reloads to flush stale persisted data.
|
|
ver: ver,
|
|
log_lvl: 0,
|
|
|
|
disable_sys_header: false,
|
|
disable_sys_nav: false,
|
|
disable_sys_footer: false,
|
|
|
|
sys_menu: {
|
|
expand: false,
|
|
focus_passcode_input: false
|
|
},
|
|
|
|
debug_menu: {
|
|
hide_quick_info: true
|
|
},
|
|
|
|
app_cfg: {
|
|
show_element__passcode_input: true
|
|
},
|
|
|
|
ds: {
|
|
submit_status: null
|
|
},
|
|
ds_loaded: {
|
|
hub__site__root_page_header: false,
|
|
hub__site__root_page_content: false,
|
|
hub__site__root_page_footer: false
|
|
},
|
|
|
|
files: {
|
|
disable_submit__hosted_file_obj: false,
|
|
processed_file_kv: {},
|
|
uploaded_file_kv: {},
|
|
video_clip_file_kv: {},
|
|
processed_file_list: [],
|
|
status__file_list: null,
|
|
video_clip_file_list: [],
|
|
submit_status: null,
|
|
clip_complete: null
|
|
},
|
|
|
|
hub: {
|
|
show_xyz: null,
|
|
account_id_qry_status: null,
|
|
event_badge_id_status_qry__search: null,
|
|
event_presenter_id_qry_status: null,
|
|
site_domain_id_qry_status: null,
|
|
sponsorship_id_qry_status: null,
|
|
sponsorship_cfg_id_qry_status: null,
|
|
|
|
qr: {}
|
|
},
|
|
mod: {
|
|
archives: {},
|
|
events: {},
|
|
journals: {},
|
|
posts: {},
|
|
sponsorships: {
|
|
disable_submit__sponsorship_obj: false,
|
|
slct__level_num: 0,
|
|
show_question__accommodations: false,
|
|
submit_status: null // 'saving', 'created', 'updated', 'saved'
|
|
},
|
|
testing: {}
|
|
},
|
|
|
|
person: {
|
|
show_report__person_li: false,
|
|
|
|
qry_limit__people: 100
|
|
},
|
|
|
|
show__modal_change_password: false,
|
|
|
|
download: {},
|
|
// Per-file progress keyed by file_id (downloads) or temp_id (uploads).
|
|
// Shape: { status, endpoint, filename, size_total, size_loaded, percent_completed }
|
|
api_download_kv: {},
|
|
api_upload_kv: {},
|
|
|
|
test: true
|
|
};
|
|
export const ae_sess = writable(ae_app_session_data_defaults);
|
|
|
|
export const ae_api_data_struct: key_val = {
|
|
ver: '2024-08-11_11',
|
|
fqdn: api_server_fqdn,
|
|
base_url: api_base_url,
|
|
base_url_bak: api_base_url_bak,
|
|
api_secret_key: api_secret_key,
|
|
api_secret_key_bak: api_secret_key,
|
|
api_crud_super_key: api_crud_super_key, // Used for operations that bypass normal account-scoped CRUD limits.
|
|
headers: {},
|
|
account_id: ae_account_id
|
|
};
|
|
|
|
const ae_api_headers: key_val = {};
|
|
ae_api_headers['Content-Type'] = 'application/json';
|
|
ae_api_headers['x-aether-api-key'] = ae_api_data_struct.api_secret_key;
|
|
if (ae_account_id) {
|
|
ae_api_headers['x-account-id'] = ae_account_id;
|
|
}
|
|
if (ae_no_account_id) {
|
|
ae_api_headers['x-no-account-id'] = ae_no_account_id;
|
|
}
|
|
ae_api_data_struct['headers'] = ae_api_headers;
|
|
|
|
export const ae_api = writable(ae_api_data_struct);
|
|
|
|
// General-purpose reactive trigger. Set any truthy value to signal consumers to re-query.
|
|
const ae_trig_template: key_val = {};
|
|
export const ae_trig = writable(ae_trig_template);
|
|
|
|
/* *** BEGIN *** Initialize slct and slct_trigger */
|
|
/* The slct and slct_trigger variable should not be stored in local storage. Only use session storage because browser tabs can be open to different accounts, events, sponsorships, etc. */
|
|
|
|
// Intended for temporary session storage.
|
|
// Updated 2024-03-15
|
|
const slct_obj_template: key_val = {
|
|
account_id: ae_account_id,
|
|
account_obj: {},
|
|
event_id: null,
|
|
event_obj: {},
|
|
event_obj_li: [],
|
|
event_presentation_id: null,
|
|
event_presentation_obj: {},
|
|
event_presentation_obj_li: [],
|
|
event_presenter_id: null,
|
|
event_presenter_obj: {},
|
|
event_presenter_obj_li: [],
|
|
event_session_id: null,
|
|
event_session_obj: {},
|
|
event_session_obj_li: [],
|
|
sponsorship_id: null,
|
|
sponsorship_obj: {},
|
|
sponsorship_obj_li: [],
|
|
sponsorship_cfg_id: null,
|
|
sponsorship_cfg_obj: {},
|
|
sponsorship_cfg_obj_li: [],
|
|
post_id: null,
|
|
post_obj: {},
|
|
post_obj_li: [],
|
|
post_comment_id: null,
|
|
post_comment_obj: {},
|
|
post_comment_obj_li: []
|
|
};
|
|
// In-memory only (not persisted) — separate browser tabs can have different selections.
|
|
export const slct = writable(slct_obj_template);
|
|
|
|
// Increment (or set any truthy value) to tell subscribers that the selection changed
|
|
// and they should re-query their data.
|
|
export const slct_trigger: any = writable(null);
|
|
|
|
// Auth error signal — set by API helpers on 401/403 to trigger the session-expired banner in the root layout.
|
|
// Only set from browser context (never SSR). 'expired' covers both 401 and 403 responses.
|
|
export const ae_auth_error = writable<{
|
|
type: 'expired' | null;
|
|
ts: number | null;
|
|
}>({ type: null, ts: null });
|
|
|
|
// Reactive clock — updates every second. Used for live countdown and elapsed-time displays.
|
|
export const time = readable(new Date(), function start(set) {
|
|
const interval = setInterval(() => {
|
|
set(new Date());
|
|
}, 1000);
|
|
|
|
return function stop() {
|
|
clearInterval(interval);
|
|
};
|
|
});
|