FIX: reset tất cả về mặc định khi tạo new project

This commit is contained in:
2026-08-06 11:47:10 +07:00
parent e99e54773f
commit 4dfab954d8
5 changed files with 104 additions and 5 deletions
+29
View File
@@ -259,3 +259,32 @@ async def get_profile(current_user: dict = Depends(get_current_user)):
"max_tracks": row["max_tracks"] or 16
}
}
@router.get("/first-time")
async def auth_first_time():
# "Lần đăng nhập đầu" = tài khoản admin vẫn dùng mật khẩu MẶC ĐỊNH
# (chưa từng đổi). Sau khi đổi lần đầu → first_time = false → UI xóa
# gợi ý username/mật khẩu (Tùy chọn - Admin có thể bỏ trống, lần đầu:
# admin123, nút Điền nhanh).
try:
conn = get_db_connection()
try:
cur = conn.cursor()
cur.execute(
"SELECT id, hashed_password, must_change_password FROM users "
"WHERE LOWER(role) = 'admin' ORDER BY created_at ASC LIMIT 1"
)
row = cur.fetchone()
finally:
conn.close()
if not row:
return {"first_time": True}
still_default = False
try:
still_default = verify_password("admin123", row["hashed_password"])
except Exception:
still_default = False
return {"first_time": bool(row["must_change_password"]) and still_default}
except Exception:
return {"first_time": True}
+43 -3
View File
@@ -4850,12 +4850,27 @@ const AuthModal = ({
const [newPassword, setNewPassword] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const [firstTime, setFirstTime] = useState(true);
useEffect(() => {
if (mode) setActiveTab(mode);
if (mode === 'force_change' && !oldPassword) {
setOldPassword('admin123');
}
}, [mode]);
// Ln đăng nhp ĐU (admin còn mt khu mc đnh) hin gi ý
// username/mt khu; sau khi đã đi n vĩnh vin.
useEffect(() => {
if (!isOpen) return;
if (window.SonicAPI && window.SonicAPI.apiRequest) {
window.SonicAPI.apiRequest('/api/v1/auth/first-time', { method: 'GET' })
.then(function (d) { setFirstTime(!!(d && d.first_time)); })
// Fail-closed: endpoint li/404 (backend chưa restart) N gi ý
// (không hin user đã yêu cu b gi ý sau ln đu).
.catch(function () { setFirstTime(false); });
} else {
setFirstTime(false);
}
}, [isOpen]);
const handleSubmit = async e => {
e.preventDefault();
setError('');
@@ -4930,7 +4945,7 @@ const AuthModal = ({
className: "w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-2 text-sm focus:outline-none focus:border-teal-500"
}))) : /*#__PURE__*/React.createElement(React.Fragment, null, activeTab === 'login' ? /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", {
className: "block text-xs font-semibold mb-1 text-slate-400"
}, "Tên đăng nhập ", /*#__PURE__*/React.createElement("span", {
}, "Tên đăng nhập ", firstTime && /*#__PURE__*/React.createElement("span", {
className: "text-teal-400 font-normal"
}, "(Tùy chọn - Admin có thể bỏ trống)")), /*#__PURE__*/React.createElement("input", {
type: "text",
@@ -4956,7 +4971,7 @@ const AuthModal = ({
className: "w-full bg-[#1e1e1e] border border-[#383838] rounded px-3 py-2 text-sm focus:outline-none focus:border-teal-500"
})), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", {
className: "block text-xs font-semibold mb-1 text-slate-400"
}, "Mật khẩu ", activeTab === 'login' && /*#__PURE__*/React.createElement("span", {
}, "Mật khẩu ", activeTab === 'login' && firstTime && /*#__PURE__*/React.createElement("span", {
className: "text-amber-400 font-normal"
}, "(Lần đầu: admin123)")), /*#__PURE__*/React.createElement("input", {
type: "password",
@@ -4969,7 +4984,7 @@ const AuthModal = ({
type: "submit",
disabled: loading,
className: "w-full py-2 bg-teal-600 hover:bg-teal-500 text-white font-semibold rounded-lg shadow transition duration-150"
}, loading ? 'Đang xác thực...' : isForceMode ? 'Đổi Mật Khẩu Ngay' : activeTab === 'login' ? 'Đăng Nhập System' : 'Tạo Tài Khoản Mới'), activeTab === 'login' && !isForceMode && /*#__PURE__*/React.createElement("button", {
}, loading ? 'Đang xác thực...' : isForceMode ? 'Đổi Mật Khẩu Ngay' : activeTab === 'login' ? 'Đăng Nhập System' : 'Tạo Tài Khoản Mới'), activeTab === 'login' && !isForceMode && firstTime && /*#__PURE__*/React.createElement("button", {
type: "button",
onClick: () => {
setUsername('admin');
@@ -15832,6 +15847,16 @@ const App = () => {
}
if (ctrl && !alt && e.key === 'n') {
e.preventDefault();
// New project: đóng hết tab + reset MI trng thái v mc đnh
try { stopAllPlayback(); } catch (e) {}
setSubTabs([]);
setSessionTabs([]);
setActiveTab('main');
trackMidiChannelsRef.current = {};
Object.keys(trackMidiBypassMap).forEach(function (k) { delete trackMidiBypassMap[k]; });
Object.keys(trackAudioBypassMap).forEach(function (k) { delete trackAudioBypassMap[k]; });
Object.keys(trackMasteringBypassMap).forEach(function (k) { delete trackMasteringBypassMap[k]; });
setSelectedItemIds(new Set());
setTracks([{
id: '1',
name: 'Track 01',
@@ -15858,6 +15883,10 @@ const App = () => {
serverFileId: null
}]);
setSelectedTrackId('1');
setProjectName('');
setCurrentProjectId(null);
localStorage.removeItem('sonic_project_name');
localStorage.removeItem('sonic_project_id');
showToast('New project created', 'info');
return;
}
@@ -24388,6 +24417,17 @@ STRICT CONSTRAINTS:
icon: 'file-plus',
shortcut: 'Ctrl+N',
action: () => {
// New project: đóng hết tab + reset MI trng thái v mc đnh
try { stopAllPlayback(); } catch (e) {}
setSubTabs([]);
setSessionTabs([]);
setActiveTab('main');
// Reset instrument/channel/route context đã load
trackMidiChannelsRef.current = {};
Object.keys(trackMidiBypassMap).forEach(function (k) { delete trackMidiBypassMap[k]; });
Object.keys(trackAudioBypassMap).forEach(function (k) { delete trackAudioBypassMap[k]; });
Object.keys(trackMasteringBypassMap).forEach(function (k) { delete trackMasteringBypassMap[k]; });
setSelectedItemIds(new Set());
setTracks([{
id: '1',
name: 'Track 01',
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -24,7 +24,7 @@
<script src="/static/js/services/midiExtractor.js?v=202607281052"></script>
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
<script src="/static/js/app.precompiled.js?v=202608061030" defer></script>
<script src="/static/js/app.precompiled.js?v=202608061200" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style>
:root {