List each account's statement files, and open them
The Accounts screen gets a Statements list: every file in every account folder beside what the index made of it -- imported, changed since, not imported yet (or failed), or gone from disk while its rows remain -- with its size, modified and import times, and how many transactions it brought in. Clicking a name opens the file. The list is read from the folders, not the index, through importer.StatementFiles, so a file shows exactly when import would read it; store.SourceFiles and importer.Checksum then say how far each has got. A file the index remembers but the disk lost is listed as missing rather than vanishing, since its rows would not survive a rebuild. A file is served only by finding it in that list, never by joining the requested name onto a path. Statements come from outside and are served from the app's origin, so none is rendered as a page: text is text/plain under CSP sandbox, anything not text or PDF is a sandboxed download, and PDFs -- whose viewers refuse a sandbox -- open in the browser's own isolated viewer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -101,7 +101,7 @@ func Run(root string, db *store.DB, accounts []*config.Account, engine *rules.En
|
||||
continue
|
||||
}
|
||||
|
||||
files, err := statementFiles(acc)
|
||||
files, err := StatementFiles(acc)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -134,7 +134,7 @@ func importFile(root string, db *store.DB, acc *config.Account, accountID int64,
|
||||
}
|
||||
fr := FileResult{Account: acc.Slug, Path: rel}
|
||||
|
||||
sum, err := checksum(path)
|
||||
sum, err := Checksum(path)
|
||||
if err != nil {
|
||||
fr.Err = err
|
||||
return fr
|
||||
@@ -252,10 +252,11 @@ func fingerprint(key string, ordinal int) string {
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
// statementFiles lists the files in an account folder that should be parsed:
|
||||
// StatementFiles lists the files in an account folder that should be parsed:
|
||||
// every regular file except account.toml and dotfiles, narrowed by the
|
||||
// account's optional include globs.
|
||||
func statementFiles(acc *config.Account) ([]string, error) {
|
||||
// account's optional include globs. It is also what the web app lists and
|
||||
// serves, so a file is shown exactly when import would read it.
|
||||
func StatementFiles(acc *config.Account) ([]string, error) {
|
||||
entries, err := os.ReadDir(acc.Dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read account dir %s: %w", acc.Dir, err)
|
||||
@@ -284,7 +285,9 @@ func matchAny(patterns []string, name string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func checksum(path string) (string, error) {
|
||||
// Checksum is the sha256 a statement is recorded under; an import skips a file
|
||||
// whose checksum has not changed.
|
||||
func Checksum(path string) (string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
Reference in New Issue
Block a user