Commit graph

744 commits

Author SHA1 Message Date
Johnny
af6a0726d8 chore: drop redundant indexes in sqlite migration 2026-01-08 22:53:46 +08:00
Johnny
1985205dc2 chore: remove memo_organizer table 2026-01-08 22:28:13 +08:00
Johnny
f4cf2e9559 test: improve migration tests stability and maintainability
- Fix linting issues and address testcontainers deprecation in store/test/containers.go
- Extract MemosStartupWaitStrategy for consistent container health checks
- Refactor migrator_test.go to use a consolidated testMigration helper, reducing duplication
- Add store/test/Dockerfile for optimized local test image builds
2026-01-07 23:04:12 +08:00
Johnny
14fb38f375 fix: attachment table name 2026-01-07 20:44:21 +08:00
Johnny
c29c1d3e1f fix: seed data 2026-01-07 20:41:16 +08:00
Johnny
cc9a214be8 revert: revert system_setting to instance_setting rename changes
Reverts only the system_setting → instance_setting rename related changes from commit d326c71.
Keeps the resource → attachment rename changes intact.

- Reverts table name back to system_setting in all database drivers (MySQL, PostgreSQL, SQLite)
- Removes migration files for the system_setting rename
- Reverts LATEST.sql files to use system_setting table
2026-01-07 20:38:02 +08:00
Johnny
e75862de31 chore: fix tests 2026-01-07 09:17:34 +08:00
Johnny
64b487d4af chore: fix seed data 2026-01-07 08:34:42 +08:00
Johnny
d326c71078 refactor(db): rename tables for clarity - resource→attachment, system_setting→instance_setting 2026-01-06 23:36:42 +08:00
Johnny
d68ca84832 fix: delete unused attachments by using filter 2026-01-05 22:43:57 +08:00
Johnny
bd02de9895
chore: add store tests (#5397) 2025-12-31 21:26:35 +08:00
Johnny
5d677828a6 refactor: remove NSFW content settings from instance configuration 2025-12-29 20:13:54 +08:00
Johnny
78aa41336a feat: implement attachment filtering functionality 2025-12-28 18:47:59 +08:00
Steven
d0c3908168 refactor: remove deprecated Sessions and AccessTokens settings
- Remove ListSessions and RevokeSession RPC endpoints
- Remove Session message and SessionsSetting from UserSetting
- Remove ACCESS_TOKENS key and AccessTokensSetting
- Update references to use RefreshTokensUserSetting with its own ClientInfo
- Remove UserSessionsSection frontend component
- Clean up user store to remove session and access token settings
- Regenerate protobuf files

The system now uses:
- REFRESH_TOKENS for session management with sliding expiration
- PERSONAL_ACCESS_TOKENS for long-lived API tokens
2025-12-19 08:35:57 +08:00
Johnny
7932f6d0d0
refactor: user auth improvements (#5360) 2025-12-18 18:15:51 +08:00
Johnny
310590b278 chore: fix golang checks 2025-12-16 22:53:36 +08:00
Johnny
40e129b8af refactor(auth): streamline session authentication and cookie handling 2025-12-16 22:23:59 +08:00
Steven
0610257562 refactor(store): remove deprecated migration_history table and backward compatibility code
Complete removal of migration_history system in favor of instance_setting based schema versioning.

Changes:
- Remove migration_history table creation from all LATEST.sql files
- Delete all migration_history model and implementation files (~300 lines)
- Remove FindMigrationHistoryList and UpsertMigrationHistory from Driver interface
- Replace complex backward compatibility functions with simple version check
- Update health check to use instance_setting instead of migration_history
- Simplify checkMinimumUpgradeVersion to detect pre-v0.22 installations

Breaking change:
Users on versions < v0.22.0 (May 2024) must upgrade to v0.25.x first before upgrading to this version.
Clear error message with upgrade instructions will be shown for old installations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 22:54:30 +08:00
Steven
d1492007ab fix(store): filter inbox notifications by message type at database level
Add MessageType filter to FindInbox to exclude legacy VERSION_UPDATE
notifications from inbox queries. This resolves the issue where users
saw notification counts but no items displayed, as VERSION_UPDATE
entries cannot be rendered in the new UserNotification API.

Changes:
- Add MessageType field to FindInbox struct for database-level filtering
- Implement JSON extraction filters in SQLite, MySQL, and PostgreSQL drivers
- Update ListUserNotifications to filter MEMO_COMMENT type at store level

This approach improves performance by filtering at the database rather
than in application code, reducing unnecessary data transfer for users
with many legacy inbox entries.

Fixes #5278

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 08:43:05 +08:00
Steven
07a030ddfd fix(postgres): update tag filtering SQL to ensure proper type casting for LIKE comparisons 2025-11-26 23:04:07 +08:00
Steven
50f49fc00d chore: update demo data 2025-11-26 21:58:56 +08:00
Steven
424f11f227 fix(store): fix PostgreSQL tag filtering type inference error
Resolves issue where tag filtering in PostgreSQL databases failed with "operator does not exist: jsonb ~~ unknown" error. The hierarchical tag filtering feature introduced in commit 5e47f25b generated SQL with implicit type placeholders that PostgreSQL couldn't infer.

The fix explicitly casts the LIKE comparison placeholder to text (::text) in the PostgreSQL dialect, ensuring proper type resolution for the query parameter.

Fixes #5275

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 07:40:39 +08:00
Steven
d1b2c0308b chore: add demo user access token for testing purposes 2025-11-11 08:31:05 +08:00
boojack
21d31e3609
fix(security): implement security review recommendations (#5228)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-06 23:32:27 +08:00
Steven
4c1d1c70d1 refactor: rename workspace to instance throughout codebase
Remove work-related terminology by renaming "workspace" to "instance"
across the entire application. This change better reflects that Memos
is a self-hosted tool suitable for personal and non-work use cases.

Breaking Changes:
- API endpoints: /api/v1/workspace/* → /api/v1/instance/*
- gRPC service: WorkspaceService → InstanceService
- Proto types: WorkspaceSetting → InstanceSetting
- Frontend translation keys: workspace-section → instance-section

Backend Changes:
- Renamed proto definitions and regenerated code
- Updated all store layer methods and database drivers
- Renamed service implementations and API handlers
- Updated cache from workspaceSettingCache to instanceSettingCache

Frontend Changes:
- Renamed service client: workspaceServiceClient → instanceServiceClient
- Updated all React components and state management
- Refactored stores: workspace.ts → instance.ts
- Updated all 32 locale translation files

All tests pass and both backend and frontend build successfully.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 23:35:35 +08:00
Steven
bc1550e926 refactor(api): migrate inbox functionality to user notifications
- Remove standalone InboxService and move functionality to UserService
- Rename inbox to user notifications for better API consistency
- Add ListUserNotifications, UpdateUserNotification, DeleteUserNotification methods
- Update frontend components to use new notification endpoints
- Update store layer to support new notification model

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 08:33:09 +08:00
Steven
5e47f25bf5 feat(store): add hierarchical tag filtering support
Tag filters now support hierarchical matching where searching for a tag (e.g., "book") will match both the exact tag and any tags with that prefix (e.g., "book/fiction", "book/non-fiction"). This applies across all database backends (SQLite, MySQL, PostgreSQL) with corresponding test updates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 00:21:53 +08:00
Steven
f635d85bcf chore: fix reactions seed data
- Add comprehensive inline documentation for auth services
- Document session-based and token-based authentication flows
- Clarify authentication priority and validation logic
- Add detailed comments for JWT token structure and claims
- Fix reactions seed data to use memo UIDs instead of numeric IDs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 20:27:27 +08:00
Claude
f241b590a2 chore: update demo data 2025-10-27 09:20:26 +08:00
Steven
e0b1153269 fix(web): resolve MobX observable reactivity issue in filter computation
Fixes filtering functionality that was broken due to improper use of
useMemo with MobX observables. The issue occurred because useMemo's
dependency array uses reference equality, but MobX observable arrays
are mutated in place (reference doesn't change when items are added/removed).

Changes:
- Remove useMemo from filter computation in Home, UserProfile, and Archived pages
- Calculate filters directly in render since components are already MobX observers
- Fix typo: memoFitler -> memoFilter in Archived.tsx

This ensures filters are recalculated whenever memoFilterStore.filters changes,
making tag clicks and other filter interactions work correctly.

Fixes #5189

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 06:59:13 +08:00
Steven
46ce0bc62e fix(store): correct PostgreSQL placeholder generation in IN clauses
Fixes a regression introduced in v0.25.2 where PostgreSQL IN clause
placeholders were not properly incremented, causing all parameters to
use the same placeholder index (e.g., $1, $1, $1 instead of $1, $2, $3).

This bug affected:
- ListReactions (ContentIDList) - caused "failed to list reactions" errors
- ListAttachments (MemoIDList)
- ListMemos (IDList and UIDList)

The fix combines placeholder generation and argument appending into a
single loop to ensure proper incrementing.

Fixes #5188

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 06:47:06 +08:00
Claude
e35f16306e feat: add id as final tie-breaker in ListMemos 2025-10-23 19:44:41 +08:00
Steven
2d4361d5fe chore: update memo content formatting and enhance link styling 2025-10-22 08:50:09 +08:00
Steven
d8f7a4e739 chore: tweak demo data 2025-10-21 23:41:46 +08:00
Steven
b19e736f10 chore: tweak demo data 2025-10-20 23:58:49 +08:00
Steven
b4ea7d843f feat: enhance memo sorting functionality to support multiple fields 2025-10-20 23:41:58 +08:00
Steven
95de5cc700 refactor: update migration history methods 2025-10-20 23:05:50 +08:00
Johnny
bc7decf642 refactor: remove unused constants 2025-10-16 20:40:46 +08:00
Copilot
b685ffacdf refactor: memo filter
- Updated memo and reaction filtering logic to use a unified engine for compiling filter expressions into SQL statements.
- Removed redundant filter parsing and conversion code from ListMemoRelations, ListReactions, and ListAttachments methods.
- Introduced IDList and UIDList fields in FindMemo and FindReaction structs to support filtering by multiple IDs.
- Removed old filter test files for reactions and attachments, as the filtering logic has been centralized.
- Updated tests for memo filtering to reflect the new SQL statement compilation approach.
- Ensured that unsupported user filters return an error in ListUsers method.
2025-10-16 09:22:52 +08:00
Steven
c3d4f8e9d1 feat: implement user-specific SQL converter for filtering in user service 2025-09-10 21:05:26 +08:00
Johnny
7cc2df9254 chore: fix linter 2025-08-31 20:22:32 +08:00
varsnotwars
4eb5b67baf
feat: attachments by id (#5008) 2025-08-15 22:02:29 +08:00
varsnotwars
f4bdfa28a0
feat: filter/method for reactions by content_id (#4969) 2025-08-08 00:00:51 +08:00
Colin Holzman
8319516d1a
fix: boolean filters (#4966) 2025-08-06 21:06:15 +08:00
johnnyjoy
506b477d50 fix: get user by username 2025-08-04 19:56:12 +08:00
varsnotwars
fa2fa8a5d7
refactor: remove call to db for parent memo name (#4947) 2025-08-04 19:39:52 +08:00
johnnyjoy
34fb3f1514 chore: fix linter 2025-08-03 19:55:04 +08:00
Colin Holzman
aae7ec8d1f
fix: calendar filter (#4942) 2025-08-03 19:11:09 +08:00
Maximilian Krauß
4d6042e35f
fix(tags): ensure JSON array elements are properly formatted in SQL queries (#4944) 2025-08-03 19:10:59 +08:00
Andrea Marchetta
71464779dd
fix: change itemCount into an Int64 (#4945) 2025-08-03 19:07:17 +08:00
johnnyjoy
ed23cbc011 refactor: memo filter 2025-07-23 22:10:16 +08:00
Steven
1a75d19a89 fix: memo filter for sqlite 2025-07-22 23:39:52 +08:00
johnnyjoy
7098721617 chore: fix linter 2025-07-22 21:15:23 +08:00
johnnyjoy
e24f92b719 fix: tests 2025-07-22 21:13:24 +08:00
johnnyjoy
b55904a428 feat: support more filter factors 2025-07-22 19:18:08 +08:00
johnnyjoy
6d9770b9c8 chore: move filter to filters 2025-07-21 21:52:00 +08:00
johnnyjoy
7481fe10bf chore: remove order by pinned 2025-07-21 21:45:10 +08:00
Johnny
40130350c5 fix: args offset for postgres 2025-07-17 00:05:07 +08:00
Steven
834f92f67c chore: fix linter 2025-07-16 22:24:50 +08:00
Steven
0398df1d21 chore: update migrator comments 2025-07-16 21:59:37 +08:00
Johnny
3e24f8cf62
fix: seed data 2025-06-25 10:07:24 +08:00
Steven
42d1650c6d chore: tweak auth service 2025-06-24 23:31:37 +08:00
Steven
6e1b01cb68 chore: bump version 2025-06-24 23:06:57 +08:00
johnnyjoy
d6a75bba4c refactor: webhook service 2025-06-24 21:28:21 +08:00
jinjingroad
03399a6007
refactor: use the built-in max/min to simplify the code (#4781)
Signed-off-by: jinjingroad <jinjingroad@sina.com>
2025-06-24 07:12:02 +08:00
Johnny
e6e460493c refactor: general user setting 2025-06-23 23:23:57 +08:00
Johnny
9b15936873 refactor: clean unused fields 2025-06-23 22:55:14 +08:00
Johnny
778a5eb184 refactor: memo filter 2025-06-23 22:38:44 +08:00
Johnny
77b7fc4441 feat: implement user session 2025-06-22 12:46:13 +08:00
Steven
9972a77d9e refactor: memo service 2025-06-18 19:58:38 +08:00
Steven
a4920d464b refactor: attachment service part2 2025-06-18 00:09:19 +08:00
Steven
8e8e246ab2 chore: add server tests 2025-06-17 00:29:42 +08:00
Johnny
38b6c3afb9 chore: fix linter 2025-06-15 10:27:24 +08:00
Johnny
3fd29f6493 refactor: schema migrator 2025-06-15 10:23:18 +08:00
Johnny
b942643785 chore: tweak seed data 2025-06-02 20:06:45 +08:00
Steven
f1b365f928 refactor: clean packages 2025-05-29 21:44:43 +08:00
Steven
de3e55c2e6 feat: support now() time functions 2025-05-28 21:18:49 +08:00
Steven
8520e30721 fix: handle type assertion safely 2025-05-27 23:44:39 +08:00
Steven
ef6f80d925 chore: fix linter 2025-05-27 23:22:32 +08:00
Steven
81ae42518e chore: fix linter 2025-05-27 22:49:56 +08:00
Steven
ad2c5f0d05 refactor: store cache 2025-05-27 22:06:41 +08:00
Steven
b89d8f5342 feat: implement hasTaskList filter 2025-05-26 22:37:59 +08:00
johnnyjoy
f9e07a2245 feat: support update user's role 2025-05-15 19:36:15 +08:00
Steven
1f79ee575b fix: create user with avatar url for sqlite 2025-05-14 23:45:02 +08:00
johnnyjoy
2a92baf52c fix: filter args of postgres 2025-05-06 21:19:13 +08:00
Johnny
b94682dc4f
chore: fix linter 2025-04-12 22:16:32 +08:00
Johnny
18b9b9d18f
chore: implement memo filter in list memo relations 2025-04-12 22:13:18 +08:00
Johnny
08f9b18ced
fix: list memo relations 2025-04-12 22:02:13 +08:00
Johnny
3cc0e255ce fix: memo filter for postgres 2025-04-08 23:46:08 +08:00
cmuangs
d649d326ef
fix: remove errors.Wrap(nil) (#4576)
* Fix errors.Wrap(nil)

* fix resource_test failure where resource does not exist
2025-03-31 12:56:51 +08:00
Steven
9b957edd1f chore: fix linter 2025-03-17 21:52:35 +08:00
Steven
8164986fcc chore: move store test 2025-03-17 21:51:02 +08:00
Amir Ehsandar
c0643ff6fa
fix: use valid query parameters for postgres (#4515) 2025-03-15 12:59:00 +08:00
Johnny
34ab88348e chore: sort by pinned 2025-03-13 19:57:12 +08:00
Johnny
e3a4f49c5c feat: implement creator_id factor 2025-03-11 22:00:57 +08:00
Johnny
925e97882e feat: support pinned factor 2025-03-10 18:52:12 +08:00
Johnny
9107a941ca chore: update linter config 2025-02-24 22:21:07 +08:00
Johnny
f98c519834 fix: linter 2025-02-24 22:04:25 +08:00
Johnny
2aaaef79f7 chore: fix tests 2025-02-18 22:32:30 +08:00
Johnny
2dc7766fd0 fix: content contains for postgres 2025-02-18 22:28:25 +08:00
MHZ
174b8b048a
feat: support blur processing for NSFW content with customizable NSFW tags (#4407) 2025-02-16 23:35:31 +08:00
Johnny
9299c216e2 chore: move migration to 0.24.0 2025-02-16 21:02:18 +08:00
Chris Dzombak
d25f355392
chore: allow storing resource references >256 chars long in mysql (#4408)
fix: allow storing resource references >256 chars long in mysql
2025-02-16 18:20:51 +08:00
Johnny
0a429a899f chore: remove version update activity 2025-02-09 11:48:53 +08:00
wzc90
3f33ccd906
fix: update 01__memo_pinned.sql (#4370) 2025-02-06 09:08:31 +08:00
Steven
7996586488 chore: fix migration script for sqlite 2025-02-05 20:56:46 +08:00
johnnyjoy
54a48b58d7 chore: remove random field 2025-02-05 20:48:27 +08:00
johnnyjoy
07336f0392 chore: update condition concat check 2025-02-05 20:07:38 +08:00
johnnyjoy
a7ca63434b feat: validate shortcut's filter 2025-02-03 18:34:31 +08:00
johnnyjoy
ff04fdc459 feat: support more operators in filter 2025-02-03 18:23:09 +08:00
johnnyjoy
f25acfe8e2 chore: fix linter 2025-02-03 17:34:46 +08:00
johnnyjoy
e0e735d14d feat: support memo filter for mysql and postgres 2025-02-03 17:14:53 +08:00
johnnyjoy
364f005ee5 fix: filter 2025-02-03 12:44:01 +08:00
johnnyjoy
3a085f3639 chore: fix filter tests 2025-02-03 12:01:34 +08:00
johnnyjoy
8e586d348e chore: fix tag filter 2025-02-03 11:45:26 +08:00
johnnyjoy
bf5a29524a chore: remove drop index 2025-02-03 11:45:15 +08:00
johnnyjoy
14c72fa7df feat: implement shortcuts 2025-02-02 17:11:52 +08:00
johnnyjoy
b9a0c56163 feat: support more factors in filter 2025-02-02 16:12:44 +08:00
johnnyjoy
2a392b8f8d chore: fix import order 2025-02-02 13:52:45 +08:00
johnnyjoy
f31800456e chore: fix import order 2025-02-02 13:52:29 +08:00
johnnyjoy
58a867e4da fix: linter warning 2025-02-02 13:50:36 +08:00
johnnyjoy
2d731c5cc5 feat: memo filter for sqlite 2025-02-02 13:35:57 +08:00
johnnyjoy
0af08d9c06 chore: add if exists to postgres 2025-02-02 13:19:56 +08:00
Xiao Wei
37a81f9d28
fix: migrate sql error (#4338)
* fix: migrate sql error

sqlite not support postgres sql

* Apply suggestions from code review

---------

Co-authored-by: Johnny <yourselfhosted@gmail.com>
2025-02-01 22:11:17 +08:00
johnnyjoy
b0119f320a chore: tweak migration scripts 2025-02-01 11:19:36 +08:00
johnnyjoy
9bd6d17864 chore: add migration script 2025-01-31 22:00:05 +08:00
johnnyjoy
39d4d4f4f3 chore: update migrate scripts 2025-01-31 21:05:20 +08:00
johnnyjoy
d605faeffa refactor: move pinned to memo 2025-01-31 20:58:18 +08:00
johnnyjoy
2058a8ab7b chore: move references to memo property 2025-01-31 14:18:07 +08:00
Viacheslav Potoropin
7ed3a12a64
fix: internal error on pin/unpin (#4332) 2025-01-31 13:38:43 +08:00
Zero King
5ec3150e0e
fix: use correct separator in migration file name (#4327)
Fixes: e913271f15 ("refactor: move tags from property to payload (#4229)")
2025-01-30 12:26:14 +08:00
Steven
8b7e9f5443 chore: remove unused user id 2025-01-10 23:35:00 +08:00
johnnyjoy
c1498a1844 chore: retire webhook state 2025-01-10 22:24:12 +08:00
Johnny
e913271f15
refactor: move tags from property to payload (#4229)
* refactor: move tags from property to payload

* chore: fix tests

* chore: drop memo tags

* chore: update

---------

Co-authored-by: Steven <stevenlgtm@gmail.com>
2024-12-24 15:23:15 +00:00
Steven
51c80c37d1 revert: tweak reaction_type column type for mysql 2024-11-20 20:37:35 +08:00
Steven
9f2c1b2556 chore: tweak reaction_type column type for mysql 2024-11-20 20:35:03 +08:00
johnnyjoy
80785de900 chore: remove unused script 2024-11-14 22:12:00 +08:00
Steven
378b6a51c1 chore: tweak default reactions 2024-11-13 08:33:12 +08:00
RoccoSmit
bcd8856732
fix: added missing " (#4119)
Added missing "
2024-11-11 08:14:17 +08:00
RoccoSmit
3b25a6bbec
fix: handle missing reaction (#4094)
* migration version

* Updated test to match with current version set in version.go file

* updated logic to check if migration should run for fresh install

* Update test to test for version number set in version.go

* fixed test

* Revert "fixed test"

This reverts commit 4004d0e92bc16423c8c2a8c9818a9c2e4bab7fdb.

* Revert "Update test to test for version number set in version.go"

This reverts commit d304add5d673effb727c208bb26c3b0b44416e1c.

* Revert "updated logic to check if migration should run for fresh install"

This reverts commit b44dca21944b696b2839296f645f408a63c58a5d.

* Revert "Updated test to match with current version set in version.go file"

This reverts commit 3ad0d889b621a8778d5efb5a62a85386a380fac8.

* Revert "migration version"

This reverts commit b13cfc346a3c2f913ded303de5d9123aaa9e029d.

* Added default reaction if no reation found

* Fixed static check issue

* Use emoji
2024-11-06 21:11:26 +08:00
Steven
317cf06579 chore: tweak demo data 2024-10-14 23:35:19 +08:00
Steven
14712b42fa chore: add pagination to list inboxes 2024-10-14 23:32:39 +08:00
Steven
d066d46f6e chore: tweak schema version 2024-10-10 22:30:46 +08:00
Steven
d54c8065b5 chore: update demo data 2024-10-10 22:04:41 +08:00
Steven
e527b6a878 feat: move reaction type to setting 2024-10-10 21:06:32 +08:00
Steven
cf4e660f41 chore: add memo location setting 2024-09-26 22:11:37 +08:00
Steven
8b444be6ca chore: update tag search 2024-09-23 21:41:23 +08:00
Sriniwas
2db67118fc
fix: case insensetive memo search for postgres (#3938)
fix:case insensetive memo search for postgres #3892
2024-09-19 23:14:51 +08:00
johnnyjoy
1da0752922 chore: add schema version to workspace setting 2024-09-14 08:23:39 +08:00
Leo
95939ebcf1
fix: v0.22.5: start fail caused by incorrect migration file naming (#3873)
fix incorrect naming of migration file
2024-09-04 06:28:55 +08:00