source_local.go 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. package script
  2. import (
  3. "context"
  4. "os"
  5. "path/filepath"
  6. "github.com/sagernet/fswatch"
  7. "github.com/sagernet/sing-box/adapter"
  8. "github.com/sagernet/sing-box/option"
  9. E "github.com/sagernet/sing/common/exceptions"
  10. "github.com/sagernet/sing/common/logger"
  11. "github.com/sagernet/sing/service/filemanager"
  12. "github.com/dop251/goja"
  13. )
  14. var _ Source = (*LocalSource)(nil)
  15. type LocalSource struct {
  16. ctx context.Context
  17. logger logger.Logger
  18. tag string
  19. program *goja.Program
  20. watcher *fswatch.Watcher
  21. }
  22. func NewLocalSource(ctx context.Context, logger logger.Logger, options option.Script) (*LocalSource, error) {
  23. script := &LocalSource{
  24. ctx: ctx,
  25. logger: logger,
  26. tag: options.Tag,
  27. }
  28. filePath := filemanager.BasePath(ctx, options.LocalOptions.Path)
  29. filePath, _ = filepath.Abs(options.LocalOptions.Path)
  30. err := script.reloadFile(filePath)
  31. if err != nil {
  32. return nil, err
  33. }
  34. watcher, err := fswatch.NewWatcher(fswatch.Options{
  35. Path: []string{filePath},
  36. Callback: func(path string) {
  37. uErr := script.reloadFile(path)
  38. if uErr != nil {
  39. logger.Error(E.Cause(uErr, "reload script ", path))
  40. }
  41. },
  42. })
  43. if err != nil {
  44. return nil, err
  45. }
  46. script.watcher = watcher
  47. return script, nil
  48. }
  49. func (s *LocalSource) StartContext(ctx context.Context, startContext *adapter.HTTPStartContext) error {
  50. if s.watcher != nil {
  51. err := s.watcher.Start()
  52. if err != nil {
  53. s.logger.Error(E.Cause(err, "watch script file"))
  54. }
  55. }
  56. return nil
  57. }
  58. func (s *LocalSource) reloadFile(path string) error {
  59. content, err := os.ReadFile(path)
  60. if err != nil {
  61. return err
  62. }
  63. program, err := goja.Compile("script:"+s.tag, string(content), false)
  64. if err != nil {
  65. return E.Cause(err, "compile ", path)
  66. }
  67. if s.program != nil {
  68. s.logger.Info("reloaded from ", path)
  69. }
  70. s.program = program
  71. return nil
  72. }
  73. func (s *LocalSource) PostStart() error {
  74. return nil
  75. }
  76. func (s *LocalSource) Program() *goja.Program {
  77. return s.program
  78. }
  79. func (s *LocalSource) Close() error {
  80. return s.watcher.Close()
  81. }