source_local.go 2.0 KB

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