Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(apiv1): accounts api #825

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open

refactor(apiv1): accounts api #825

wants to merge 28 commits into from

Conversation

fmartingr
Copy link
Member

@fmartingr fmartingr commented Jan 27, 2024

  • Refactor Accounts API to API v1
  • Renamed current AccountsDomain to AuthDomain to handle authentication logic in a separate layer.
  • Adds AccountsDomain which contains logic related to managing Accounts and is the layer that communicates with teh database.
  • New AccountDTO object. Account is the exact database representation of the row, AccountDTO is the object we can transfer around in the rest of the code with more fields, helper methods, etc. Is also the object used in creates/updates for the Accounts with included validation.
    • Changed the Account usages around the code to be AccountDTO.
  • Database interface changes:
    • Added new Database.ListAccounts (formerly Database.GetAccounts). Using List* to better differentiate between the method that return one account and the one that return many.
      • Accepts new ListAccountOptions to filter by keyword, username and owner. Also allows retrieving the password field which is disabled by default.
    • DeleteAccount replaces DeleteAccounts. Now only deletes one account, iteration logic moved to the AccountsDomain.
    • SaveAccount is not called CreateAccount
  • New middleware: AdminRequired
  • Changed /api/v1/auth/account to allow updating the entire user data, not only the settings. This should be backwards compatible.
  • Added model.Ptr helper that return an object as a pointer of itself
  • Added ValidationError error type to return directly from domains and have details available to expose to the API.
  • Testutil additions: NewAdminUser and options.WithAuthToken to avoid boilerplate in tests.

Closes #657

@fmartingr fmartingr self-assigned this Jan 27, 2024
@fmartingr fmartingr changed the title refactor: migrate accounts API refactor(apiv1): accounts api to v1 Jun 3, 2024
@go-shiori go-shiori deleted a comment from codecov bot Jun 3, 2024
@go-shiori go-shiori deleted a comment from codecov bot Jun 3, 2024
Copy link

codecov bot commented Jun 3, 2024

Codecov Report

Attention: Patch coverage is 69.85981% with 129 lines in your changes missing coverage. Please review.

Project coverage is 38.05%. Comparing base (72aecd2) to head (ed7027e).

Files Patch % Lines
internal/domains/auth.go 8.10% 34 Missing ⚠️
internal/database/pg.go 70.96% 11 Missing and 7 partials ⚠️
internal/database/sqlite.go 70.68% 11 Missing and 6 partials ⚠️
internal/http/routes/api/v1/accounts.go 79.48% 12 Missing and 4 partials ⚠️
internal/database/mysql.go 72.00% 7 Missing and 7 partials ⚠️
internal/domains/accounts.go 76.27% 8 Missing and 6 partials ⚠️
internal/cmd/root.go 0.00% 6 Missing ⚠️
internal/testutil/accounts.go 66.66% 2 Missing and 2 partials ⚠️
internal/http/routes/api/v1/tags.go 0.00% 2 Missing ⚠️
internal/http/routes/api/v1/api.go 0.00% 1 Missing ⚠️
... and 3 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #825      +/-   ##
==========================================
+ Coverage   34.66%   38.05%   +3.39%     
==========================================
  Files          61       66       +5     
  Lines        4050     4262     +212     
==========================================
+ Hits         1404     1622     +218     
+ Misses       2424     2388      -36     
- Partials      222      252      +30     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@fmartingr fmartingr marked this pull request as ready for review June 3, 2024 17:53
@fmartingr fmartingr changed the title refactor(apiv1): accounts api to v1 refactor(apiv1): accounts api to v1 (WIP) Jun 3, 2024
@fmartingr fmartingr marked this pull request as draft June 3, 2024 21:43
@fmartingr fmartingr changed the title refactor(apiv1): accounts api to v1 (WIP) refactor(apiv1): accounts api to v1 Jun 3, 2024
@fmartingr fmartingr changed the title refactor(apiv1): accounts api to v1 refactor(apiv1): accounts api Jun 3, 2024
@fmartingr fmartingr marked this pull request as ready for review June 8, 2024 16:29
@fmartingr fmartingr requested a review from Monirzadeh June 8, 2024 16:32
Comment on lines 673 to +675
// GetAccount fetch account with matching username.
// Returns the account and boolean whether it's exist or not.
func (db *MySQLDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) {
func (db *MySQLDatabase) GetAccount(ctx context.Context, id model.DBID) (*model.Account, bool, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment say get account with matching username but you get ID in this function

Comment on lines +693 to +694
// DeleteAccount removes record with matching username.
func (db *MySQLDatabase) DeleteAccount(ctx context.Context, id model.DBID) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here again you get model.DBID that is int.

Comment on lines +593 to +598
query, err := tx.PrepareContext(ctx, `INSERT INTO account
(username, password, owner, config) VALUES ($1, $2, $3, $4)
ON CONFLICT(username) DO UPDATE SET
password = $2,
owner = $3
RETURNING id`)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as i see if conflict happen it can rewrite password.
we have an update account for this. so why you done that this way?

Comment on lines 689 to +691
// GetAccount fetch account with matching username.
// Returns the account and boolean whether it's exist or not.
func (db *PGDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) {
func (db *PGDatabase) GetAccount(ctx context.Context, id model.DBID) (*model.Account, bool, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same problem in comment that say username but get id

Comment on lines +709 to +710
// DeleteAccount removes record with matching username.
func (db *PGDatabase) DeleteAccount(ctx context.Context, id model.DBID) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Comment on lines +694 to +698
query, err := tx.PrepareContext(ctx, `INSERT INTO account
(username, password, owner, config) VALUES (?, ?, ?, ?)
ON CONFLICT(username) DO UPDATE SET
password = ?, owner = ?
RETURNING id`)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't get conflict part

Comment on lines +736 to +737
func (db *SQLiteDatabase) UpdateAccount(ctx context.Context, account model.Account) error {
if account.ID == 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a big deal but because you have comment for updateAccount in other databases please add that comment here too.

Comment on lines 797 to +799
// GetAccount fetch account with matching username.
// Returns the account and boolean whether it's exist or not.
func (db *SQLiteDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) {
func (db *SQLiteDatabase) GetAccount(ctx context.Context, id model.DBID) (*model.Account, bool, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment matching username but you get id

Comment on lines +817 to +818
// DeleteAccount removes record with matching username.
func (db *SQLiteDatabase) DeleteAccount(ctx context.Context, id model.DBID) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment should change


return d.deps.Config.Http.SecretKey, nil
})
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(account.Password), 10)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am thinking cost factor 10 for bcrypt is enough?
should we increase that? what do you think?

Comment on lines +3 to +5
func Ptr[t any](a t) *t {
return &a
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice :)

"github.com/go-shiori/shiori/internal/model"
)

func NewAdminUser(deps *dependencies.Dependencies) (*model.AccountDTO, string, error) {
Copy link
Collaborator

@Monirzadeh Monirzadeh Jun 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it can be good if add comment to explain what it is exactly do and what is the use case of that for others others.

Comment on lines +325 to +326
fetch(new URL(`api/v1/accounts/${account.id}`, document.baseURI), {
method: "patch",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why done this? why not use payload?

@@ -372,9 +355,8 @@ export default {
secondText: "No",
mainClick: () => {
this.dialog.loading = true;
fetch(`api/accounts`, {
fetch(`api/v1/accounts/${account.id}`, {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't get advantage of this?

Copy link
Collaborator

@Monirzadeh Monirzadeh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @fmartingr Thanks for this PR. you make things easear in long run and i like the way you simplify complex code.
I test this PR in different way and find some bugs in it

  • when you try to logout you will get interface conversion: interface {} is *model.AccountDTO, not model.Account (500) error
  • if you create a user as visitor it always create as owner in webui and you can't change that in webui
  • when you logout the token not remove form browser storage left last user account token in there (maybe because of bug in logout)
  • i see you change user settings (theme, showId,etc) api in api/v1/auth/account and i thinking with myself can i hack that? and yes i can change my status from visitor to owner just by settings API
    you can done that with send this request with a visitor token (the new logic of update setting let me to do that)
{
  "owner": true,
  "config": {
    "CreateEbook": true,
    "HideExcerpt": true,
    "HideThumbnail": true,
    "KeepMetadata": true,
    "ListMode": true,
    "MakePublic": true,
    "NightMode": true,
    "ShowId": true,
    "UseArchive": true
  }
}

you even can change things that should not available for the visitor user

  • i am thinking about a mechanism that disable token in server side when user logout. so if i logout i can't authorize with that token again (you can still use token even if you done a logout until it expire )
  • some part of code is not clear to me so i left comment on some of them

I always learn form your code. thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

APIv1: Accounts
2 participants